Featured image of post 第1回-ダイスを振ろう - PerlとMooでダイス言語を作ってみよう

第1回-ダイスを振ろう - PerlとMooでダイス言語を作ってみよう

TRPGで使われるダイス記法「2d6」を解釈して振る仕組みをPerlとMooで実装します。まずは最小限のDiceクラスを作り、ダイスを振る基礎を学びましょう。

このシリーズについて

ダイス言語インタプリタ

このシリーズでは、TRPGやボードゲームでおなじみの「ダイス記法」を解釈して評価するインタプリタを作ります。

「2d6+3」や「3d8*2-1」といった文字列を読み取り、実際にダイスを振って計算結果を返すツールです。全8回を通じて、オブジェクト指向設計の実践的な手法を学んでいきます。

想定読者

  • Perl入学式を卒業したばかりの方
  • 「Mooで覚えるオブジェクト指向プログラミング」シリーズを読了された方
  • モダンなPerlでオブジェクト指向プログラミングを身に付けたい方

このシリーズで得られること

  • オブジェクト指向プログラミングの原則を深く学べます
  • SOLID原則を実践的に体得できます
  • デザインパターンを自然に覚えられます
  • 「自作ダイス言語」という自慢できるアウトプットが手に入ります

ダイス記法とは

TRPGやボードゲームでは、「NdM」という形式でダイスを表現します。

  • 2d6 → 6面ダイスを2個振る
  • 1d20 → 20面ダイスを1個振る
  • 3d8 → 8面ダイスを3個振る

「d」はdice(ダイス)の頭文字で、左側の数字が振る個数、右側の数字がダイスの面の数を表します。

今回はこの「NdM」形式を解釈してダイスを振るところから始めましょう。

最小限のDiceクラスを作る

まずは、ダイスを振る機能を持つシンプルなクラスを作ります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env perl
use v5.36;

package Dice {
    use Moo;

    has count => (is => 'ro', required => 1);  # 振る数
    has sides => (is => 'ro', required => 1);  # 面の数

    sub roll($self) {
        my $total = 0;
        for (1 .. $self->count) {
            $total += int(rand($self->sides)) + 1;
        }
        return $total;
    }
}

# 使ってみる
my $dice = Dice->new(count => 2, sides => 6);
say "2d6の結果: " . $dice->roll;

my $dice20 = Dice->new(count => 1, sides => 20);
say "1d20の結果: " . $dice20->roll;

実行すると、毎回異なる結果が得られます。

1
2
2d6の結果: 8
1d20の結果: 15

Diceクラスは2つの属性を持ちます。

  • count: 振るダイスの個数
  • sides: ダイスの面の数

rollメソッドは、指定された回数だけダイスを振り、合計を返します。

ダイス記法を解析する

「2d6」という文字列から、count=2、sides=6を取り出すパーサーを追加しましょう。

 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
#!/usr/bin/env perl
use v5.36;

package Dice {
    use Moo;

    has count => (is => 'ro', required => 1);
    has sides => (is => 'ro', required => 1);

    sub roll($self) {
        my $total = 0;
        for (1 .. $self->count) {
            $total += int(rand($self->sides)) + 1;
        }
        return $total;
    }

    sub parse($class, $notation) {
        if ($notation =~ /^(\d+)d(\d+)$/) {
            my ($count, $sides) = ($1, $2);
            die "不正なダイス記法: $notation" if $count < 1 || $sides < 1;
            return $class->new(count => $count, sides => $sides);
        }
        die "不正なダイス記法: $notation";
    }
}

# 文字列から解析
my $dice = Dice->parse('2d6');
say "2d6の結果: " . $dice->roll;

my $dice2 = Dice->parse('3d8');
say "3d8の結果: " . $dice2->roll;

my $dice3 = Dice->parse('1d20');
say "1d20の結果: " . $dice3->roll;

parseメソッドは、正規表現でダイス記法を解析します。

  • (\d+)d(\d+) → 「数字 + d + 数字」の形式にマッチ
  • $1がcount、$2がsidesになる

これで「2d6」のような文字列からDiceオブジェクトを作れるようになりました。

乱数のテストについて

ダイスには乱数が絡むため、テストが難しいと思われがちです。しかし、Perlではsrand関数でシードを固定することで、再現可能なテストが書けます。

1
2
3
srand(42);  # シードを固定
my $dice = Dice->new(count => 2, sides => 6);
say $dice->roll;  # 常に同じ結果になる

開発中はシードを固定して動作確認すると便利です。

本番利用ではシード固定は解除し、毎回ランダムになるようにしておきましょう。

今回のまとめ

今回は、ダイス記法を解釈してダイスを振る最小限のDiceクラスを作りました。

  • Diceクラスでダイスの個数と面数を管理
  • rollメソッドで実際にダイスを振る
  • parseメソッドで「2d6」形式の文字列を解析

次回は、「2d6+3」のような計算式に対応しようとして、問題に直面します。

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。