Jul 28, 2011

IRCダイスボットへの道 - ダイス目や修正値を集計してみる

PerlIRCダイスボットへの道第三回は、ダイスの目や修正値を集計してみます。

Perlモジュールの Parse::RecDescent を使ってダイスコマンド文字列をパースして無名ハッシュのリファレンスに収め、Data::Transformer を使ってそのままの状態でダイスだけ転がしてみました。乱数には Perl の標準関数 の rand ではなく、Math::Random::MT を使ってメルセンヌ・ツイスタを利用しています。前回との違いは、集計用のサブルーチンを追加して集計結果を表示させてる部分だけです。

diceroll.pl

#!/usr/bin/env perl

use strict;
use warnings;
use Parse::RecDescent;
use Data::Transformer;
use Math::Random::MT;
use Data::Dumper;

my $grammar = <<'GRAMMER';
expression: add end { { left => $item[1] } }
add: mult '+' add { { left => $item[1], op => '+', right => $item[3] } }
add: mult '-' add { { left => $item[1], op => '-', right => $item[3] } }
add: mult
mult: brack '*' mult { { left => $item[1], op => '*', right => $item[3] } }
mult: brack
brack: '(' add ')' { $item[2] }
brack: val
val: /[1-9]\d?d(?:100|20|12|10|8|6|4|2)/i
val: /\d{1,2}/
end: /\s*$/
GRAMMER

my $parser = Parse::RecDescent->new( $grammar ) or die 'Bad grammar';

my $text = $ARGV[0];
my $result = $parser->expression( $text ) or die 'Bad text';

my $seed = time ^ $$;
my $gen  = Math::Random::MT->new($seed);

my $roll =  Data::Transformer->new(
    normal => sub {
                  my $val = shift;
	          if ( $$val =~ /^([1-9]\d?)d(100|20|12|10|8|6|4|2)$/i ) {
                      my ( $dice_counts, $dice_planes ) = ( $1, $2 );
                      my $dices;
                      while ( $dice_counts ) {
	                  my $a_dice = int $gen->rand( $dice_planes ) + 1;
                          $dices += $a_dice;
                          $dice_counts--;
                      }
                      $$val = $dices;
                  }
              },
    );
$roll->traverse( $result );

local $Data::Dumper::Sortkeys = 1;
my $d = Data::Dumper->new( [ $result->{ left } ] );
print $d->Dump;

print &gathering( $result->{ left } ), "\n";

sub gathering {
    my $val = shift;
    if ( ref $val ) {
        if ( $val->{ op } =~ /\+/ ) {
            &gathering( $val->{ left } ) + &gathering( $val->{ right } );
	}
        elsif ( $val->{ op } =~ /-/ ) {
            &gathering( $val->{ left } ) - &gathering( $val->{ right } );
        }
        else {
            &gathering( $val->{ left } ) * &gathering( $val->{ right } );
        }
    }
    else {
        $val;
    }
}
$ ./diceroll.pl "(1d8+3d6+1d100-2)*2"
$VAR1 = {
          'left' => {
                      'left' => 3,
                      'op' => '+',
                      'right' => {
                                   'left' => 11,
                                   'op' => '+',
                                   'right' => {
                                                'left' => 20,
                                                'op' => '-',
                                                'right' => '2'
                                              }
                                 }
                    },
          'op' => '*',
          'right' => '2'
        };
64

なんとなく強引な気はしますけども、これで一応ローカルダイスが完成した事にしてしまいます。

Posted at 00:38 in perl | Comments/Trackbacks ()
Comments/Trackbacks
TrackBack ping me at
http://pochi.usamimi.info/blog/perl/dice_roll_next.
Post a comment

writeback message: