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>