Catalystの練習 - じゃんけん大会を作ってみる
1行伝言板の再実装がまだ完成してないですが、先に骨格動作ができたのでメモ。
動作サンプルを設置できたらいいんだけど、Perlモジュールいろいろ入れられて、MySQLも使える無料レンタルサーバってないかなぁ(・ω・; (自宅鯖を公開運用する気合いはないらしい)
コントローラ(Root.pm)がこんな感じ。
sub default : Private { my ( $self, $c ) = @_; $c->forward('make_member_list'); # 登録者名簿をstashに格納 $c->forward('make_hand_list'); # じゃんけんデータをstashに格納 $c->forward('tournament'); # 勝敗表の作成 $c->stash->{template} = 'index.tt'; $c->forward('spr::View::TT'); } sub post : Global { my ( $self, $c ) = @_; # 大会参加登録画面を表示 $c->stash->{template} = 'post.tt'; $c->forward('spr::View::TT'); } sub regist : Global { my ( $self, $c ) = @_; # データベースに登録 for(my $i = 0; $i < 10; $i++){ last if ($c->req->param("hand$i") eq ""); # ↓とりあえず無条件insertにしてるけど、名前かぶりのチェックとかいるよな spr::Model::CDBI::Spr->insert({ name => $c->req->param('name'), count => $i, hand => $c->req->param("hand$i") }); } $c->res->redirect('/'); } sub match : Private { my ( $self, $c ) = @_; my %judge; my $i; my $count1 = 0; my $count2 = 0; my $player1 = $c->stash->{'player1'}; my $player2 = $c->stash->{'player2'}; my $hand1; my $hand2; my $index; # $c->stash->{'test'} = $player1; $c->stash->{'winner'} = $player1; # 最後まであいこなら、player1勝利 %judge = ( "ss" => 'draw', # チョキxチョキ "sp" => $player1, # チョキxパー "sr" => $player2, # チョキxグー "ps" => $player2, # パー xチョキ "pp" => 'draw', # パー xパー "pr" => $player1, # パー xグー "rs" => $player1, # グー xチョキ "rp" => $player2, # グー xパー "rr" => 'draw' # グー xグー ); $c->stash->{'score'} .= "<table border=1 cellspacing=0 cellpadding=0>\n"; # 勝敗表の作成 $index = "<tr><td>NAME</td>"; $hand1 = "<tr><td>$player1</td>"; $hand2 = "<tr><td>$player2</td>"; for ($i = 0; $i < 10; $i++){ my $a = $c->stash->{'hand'}{"$player1$count1"}; my $b = $c->stash->{'hand'}{"$player2$count2"}; $index .= "<td>$i</td>"; $hand1 .= "<td>$a</td>"; $hand2 .= "<td>$b</td>"; if ($a ne $b){ $c->stash->{'winner'} = $judge{"$a$b"}; last; } $count1++; $count2++; $count1 = ($c->stash->{'hand'}{"$player1$count1"} ne "") ? $count1 : 0; # 次の手がなければ、先頭に戻る(Player1) $count2 = ($c->stash->{'hand'}{"$player2$count2"} ne "") ? $count2 : 0; # 次の手がなければ、先頭に戻る(Player2) } $index .= "</tr>\n"; $hand1 .= "</tr>\n"; $hand2 .= "</tr>\n"; $c->stash->{'score'} .= $index . $hand1 . $hand2 ."</tr></table><br>\n"; } sub make_hand_list :Private { my ( $self, $c ) = @_; my @hands; my $name; my $i; my %hand_list; # { 名前番号 => 出す手 } my $j; for ($j = 0; $j < $#{$c->stash->{'member'}} + 1; $j++){ $name = ${$c->stash->{'member'}}[$j]->get('name'); @hands = spr::Model::CDBI::Spr->search_like(name => $name); for($i = 0; $i < $#hands + 1; $i++){ $hand_list{"$name$i"} = $hands[$i]->get('hand'); } } $c->stash->{'hand'} = \%hand_list; # $c->stash->{'test'} = $c->stash->{'hand'}{"USER10"}; } sub make_member_list : Private { my ( $self, $c ) = @_; my $name; my $i = 0; my %name_to_num; my @member = spr::Model::CDBI::Spr->search_like(count => 0); for($i = 0; $i < $#member + 1; $i++){ $name_to_num{$member[$i]->get('name')} = $i; } $c->stash->{'member'} = \@member; # 登録者名簿( 名前 )のリスト $c->stash->{'name_to_num'} = \%name_to_num; # 登録者名簿{ 名前 => 番号 }のハッシュ } sub tournament : Private { my ( $self, $c ) = @_; my $max = 2; my $num_of_members = $#{$c->stash->{'member'}} + 1; my $lvl; my $i; my $j; my $n; my $p; my $wi; my $wn; my @cell; my $player1; my $player2; for( $lvl=1; $max <= $num_of_members; $lvl++){ $max *= 2; } # 参加人数をこえる最小の2^nを求める my $seads = $max - $num_of_members; # シード選手の人数 = (参加人数をこえる最小の2^n) - 参加人数 my $halfseads = int( $seads / 2 ); # シード数の1/2 my $restseads = $num_of_members - ( $seads - $halfseads ); # 残りのシード数 # セルの初期化 for( $j = 1; $j <= $lvl * 2; $j++ ){ for( $i = 0; $i < $num_of_members * 2; $i++ ){ $cell[$j][$i] = ""; } } $p = 0; # 表示内容を配列@cellに格納する for( $i = 0; $i < $num_of_members; $i++ ){ # Level 1の表示内容決定(初戦のみシード枠判定がある) $wi = $i + 1; if( $i < $halfseads || $i >= $restseads){ $cell[1][$i*2] = "─"; # 参加者が2^nの場合、全員初戦シード扱いになる $cell[2][$i*2] = $c->stash->{'member'}[$i]->get('name'); } else{ if( $p == 0 ){ # 対戦アーチ描画中でなければ $cell[1][$i*2] = "┐"; # $p = 1; # 対戦アーチ描画中フラグON } else{ $cell[1][$i*2] = "┘"; # $cell[1][$i*2-1] = "├"; # $c->stash->{'player1'} = $c->stash->{'member'}[$i-1]->get('name'); # $c->stash->{'player2'} = $c->stash->{'member'}[$i]->get('name'); # $c->forward('match'); $cell[2][$i*2-1] = $c->stash->{'winner'}; $p = 0; # 対戦アーチ描画中フラグOFF } } } for( $j = 3; $j <= $lvl * 2; $j = $j+2 ){ # Level 2以上の表示内容決定 $p = 0; for( $i = 0; $i < $num_of_members * 2; $i++ ){ if( $cell[$j-1][$i] ne "" ){ # 一段下のセルが空白でなければ描画開始 if( $p == 0 ){ # 対戦アーチ描画中でなければ $cell[$j][$i] = "┐"; # $c->stash->{'player1'} = $cell[$j-1][$i]; $p = 1; # 対戦アーチ描画中フラグON $n = 1; # 対戦アーチの長さを測定開始 } else{ $cell[$j][$i] = "┘"; # $c->stash->{'player2'} = $cell[$j-1][$i]; $wn = int( $n / 2 ); # 対戦アーチ描画終了時に、真ん中を"├"に変更 $cell[$j][$i-$wn] = "├"; # $c->forward('match'); $cell[$j+1][$i-$wn] = $c->stash->{'winner'}; $p = 0; # 対戦アーチ描画中フラグOFF } } elsif( $p == 1 ){ $cell[$j][$i] = "│"; # $n++; # 対戦アーチの長さを測定する } } } # 表示内容をstashに格納する $c->stash->{'tournament_graph'} = "<CENTER><TABLE border=0 cellspacing=0 cellpadding=0>\n"; for( $i = 0; $i < $num_of_members * 2; $i++){ # 縦幅(セル数)は、参加者*2マス $c->stash->{'tournament_graph'} .= "<TR>"; for( $j = 0; $j <= $lvl * 2; $j++){ # 最下位Levelから出力 $c->stash->{'tournament_graph'} .= "<TD>"; if( $j == 0 ){ # Level 0は参加者表示欄 if( $i % 2 == 0 ){ # 偶数欄に参加者No.を表示 $wi = int( $i / 2 ); # my $name = $c->stash->{'member'}[$wi]->get('name'); $c->stash->{'tournament_graph'} .= $c->stash->{'member'}[$wi]->get('name'); } } else{ # Level 1以上 if($cell[$j][$i] ne ""){$c->stash->{'tournament_graph'} .= $cell[$j][$i];} # その他セルは$cell[$i][$j]=0のため、何も出力しない(空白セル) } $c->stash->{'tournament_graph'} .= "</TD>\n"; } $c->stash->{'tournament_graph'} .= "</TR>"; } $c->stash->{'tournament_graph'} .= "</TABLE>"; }
ビューがこんな感じ。
index.tt
<html> <head> <title>spr-tournament</title> </head> <body> トーナメント表:[% test %]: [% tournament_graph %] <br> <a href="post">参加登録はこちら</a><br> [% score %] </body> </html>
post.tt
<html> <head> <title>spr-tournament/post</title> </head> <body> <form action="regist" method="post"> NAME: <input type="text" name="name" size="30"> <br> [% count = 0 %] [% WHILE count < 10 %] HAND[% count %]: <select name="hand[% count %]"> <option value="" selected>--選択してください-- <option value="r">グー <option value="s">チョキ <option value="p">パー </select><br> [% count = count + 1 %] [% END %] <input type="submit" value="登録"> </form> </body> </html>