jug7.com
[ home / cgi / juggler / column / diary / bbs / link / welcome ]


cgi スクリプト ソース




 「 こういうスマートな書き方があるのに…こいつアホか! 」

というご意見を頂きたいのが、ソースを公開している理由です。

なので、bbs や mail にてご指摘、ご意見を頂けると非常にありがたいです。
 mail : master at jug7.com


perl スクリプトのソース

機械割の計算
1000円でのコイン持ちの計算
BIG 獲得枚数の計算
設定判別の判別開始プレイの計算
平均RT解除プレイ数の計算
ボーナスを引ける確率とプレイ数
ハマリからの設定推測
継続率による期待値
二項分布
テーブルタグ吐き出し
テーブルタグ吐き出し(AT機)
テーブルタグ吐き出し(ジャグ用)
テーブルタグ吐き出し(北斗の拳用)
テーブルタグ吐き出し(秘宝伝用)
ジャグラー設定信頼度
ジャグラー設定信頼度 2
アイムジャグラー設定信頼度
スパイダーマン2 設定推測
パチスロ簡易計算機


青ドン
アイムジャグラーEX
サンダーV スペシャル
クランキーコンドル X
サクラ大戦
ハイサイ潮姫

吉宗
押忍!番長
秘宝伝
秘宝伝 BIG中設定判別
秘宝伝 BIG中シミュレーション
アラジン 2 エボリューション
北斗の拳
南国育ち

ノーマル機:スロット
GOGOジャグラーSP
ハナビ
ニューパルサーR
A-400 type 4機種
サンダーV 設定判別

ボンバーパワフル
ストリートファイター2
キングオブキングパルサー
キングパルサー
キングパルサー 設定判別
ミリオンゴッド
アステカ
獣王
アラジンA
スーパーリノ
ネオプラネット
カンフーレツデン
海一番
花伝説-30
バベル


cginame , title
送信データの読みこみ
html 表示
エラーチェック
シミュでのメインループ
結果の表示
ストック機の役抽選
初期ストックがある場合
RTプレイ数の抽選
桁揃えと四捨五入


--------------------------------------------------------------------------------

cginame , title


1:
2:

$cginame = 'wari.cgi';
$title   = '機械割の計算';

cgi なので、html 表示の form のところで

 <form action='wari.cgi' method='POST'>

のような記述を必ずします。

cgi のファイル名を変えたくなった時に、$cginame = 'wari.cgi'; としておけば、
この部分だけを変えればいいので便利かな、と思ってこのように書きました。

$title についても同様です。

( つーか、実行している cgi のファイル名って $0 に格納されてるのよね…。あぁぁ。)

topへ

--------------------------------------------------------------------------------

送信データの読みこみ


1:
2:
3:
4:
5:
6:
7:
8:

if ($ENV{'REQUEST_METHOD'} eq 'POST') {
    read (STDIN, $input, $ENV{'CONTENT_LENGTH'});
}
foreach (split(/&/,$input)) {
    ($name,$value) = split(/=/, $_);
    push(@input, $value);
}
($s, $play, $mode) = @input;


まず閲覧者から送信されてきたデータを読み込みます。この読みこみは html を表示する前に行います。

 <input type='text' name='hoge' value='$hoge'>

のように form タグの中で変数を使った場合、送信データの読みこみを先にしておかないと反映されないので。

1:〜3: では、リクエストメソッドが POST の場合、read 関数を使って、 送信データを $input に代入しています。

ここで仮に送信されたデータが  s=5&play=8000&mode=1  だとしましょう。

foreach の対象となる配列は split(/&/, $input) 、 つまり s=5 , play=8000 , mode=1 です。

foreach の中では、これらの対象をさらに = を 区切り文字として $name, $value に分けます。
$name は今後、特に使いません。 $value を push関数で @input に代入します。

foreach を抜けた時、 @input = (5,8000,1); となってるわけですね。
で、8: のところで結局、変数 $s, $play, $mode として 代入しなおしています。

一般的にこの処理は、以下のように連想配列に代入することが多いようです。


foreach (split(/&/,$input)) {
    ($key,$value) = split(/=/, $_);
    $input{$key}= $value;
}

連想配列はあまり好きではないので、変数に代入しました。

( しかし、今後は連想配列を使っていきたい… )

topへ

--------------------------------------------------------------------------------

html の表示


1:
2:
3:
4:
5:
6:
7:
8:
9:

print <<"__ FORM __";
<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>
<html lang='ja'>
<head>
<meta http-equiv='Content-Type' content="text/html; charset=shift_jis">
<title>jug7.com - cgi $title</title>
……
</center>
__ FORM __


ここはただの html 表示です。

topへ

--------------------------------------------------------------------------------

エラーチェック


1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:

if ($play > 9999)  { $error = 1; $er_msg = 'プレイ数は最大9999Pまでです。'; }
if (!$s or !$play) { $error = 1; $er_msg = '記入漏れがあります。'; }
if ($s<1 or $s>6)  { $error = 1; $er_msg = '設定は 1 から 6 までです。'; }

if ($ENV{'REQUEST_METHOD'} ne 'POST') { $error = 1; $er_msg = 0; }

if ($er_msg) {
    print '*** ERROR !! ***',"\n";
    print "$er_msg\n";
}

if ($error) {
    print '…',"\n";
    exit;
}

1:〜3: でエラーチェックをしています。エラーがある場合、
$error=1; $er_msg = '…';( つまり $er_msg も真 )となります。

7:〜10: より、er_msg が真である場合は、エラーの原因を表示します。
そして 12:〜15: で フッタを表示して exit; させています。

form method='POST' 以外でアクセスされた場合も、この後の処理を行う必要はなく
ここで exit; させるために 5: で $error=1; としています。


ちなみに4桁以上のプレイ数はブラウザで入力できないように <input type='text' name='play' maxlength=4>
と、maxlength の指定をしていますが、ブラウザ以外から POST で
 s=6&play=100000000000000000000000000000000&… のようにアクセスされるとかないませんので、
1: で 9999P 以上のプレイ数はエラーにする必要があります。

topへ

--------------------------------------------------------------------------------

シミュでのメインループ


1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:

while (1) {
    $g++; $count++;
    if ($coin < 3) { $coin += 50; $k++; $totalk++; }
    $coin -= 3;

    $r = int(rand(16384)+1);
    if ($r <= 68) {          # BIG
        $bigcount++;   $coin += $bigget;
        print "……";
        $count = 0;  $k = 0;
    } elsif ($r <= 68+45) {  # REG
        $regcount++;   $coin += $regget;
        print "……";
        $count = 0;  $k = 0;
    }

    if($g >= $play) {
        last; # ヤメ
    }
}

シミュレーション系 cgi のメインプログラムの基本形です。( 小役の当選は省略しています。)

$g は総ゲーム数、 $count はボーナスカウンタ。$coin は持ちコイン。
$k は次回ボーナスが当たるまでの投資額、 $totalk は総投資額です。

2:で $g, $count を +1 。つまり総ゲーム数とカウンタを +1 します。
パチスロは3枚掛けなので、4:で $coin から 3 だけ引きます。
もし、持ちコイン $coin が 3 より小さければ 投資 $k, $totalk を +1 する代わりに
$coin に 50 を足します
( コインサンドから1000円で 50枚 借りるわけですね。 )

そして 6:〜15: により役の抽選。 1〜16384 よりランダムな数値を選び、
7:〜15: の if 文により、BIG,REG 当選とします。 ( ここでは BIG を 68個、REG を 45個としました。)

ボーナスに当選した場合は、$coin += $bigget; のようにコインを増加させ、
print 文により $count, 当選したボーナス, $coin, $k などを表示した後で
ボーナスカウンタと今回の投資額を 0 にリセットします。

投資額をここでリセットすることで、
  プレイ数 当選 投資
     200P  BIG    7k
     400P  BIG    4k
のように、次の投資額は追い金の形で表示できます。
この例の場合、$totalk には総投資額である 11 が入っています。

このメインループは、15:〜17:において $g が 入力されたプレイ数 $play より大きくなり、
last で while ループから抜けるまで続きます。

topへ

--------------------------------------------------------------------------------

結果の表示


1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:

$toushi = $totalk * 1000;
$kankin = $coin*20;
$shushi = $kankin - $toushi;
$payout = ($coin-($totalk * 50))/($g * 3)*100+100;

print <<"__ RESULT __";
… 結果の表示

<form action='$cginame' method='POST'>
… 再試行のフォーム
</form>

…
</body></html>
__ RESULT __

exit;

1:〜4: により、投資額、換金額、収支、機械割を計算します。
6:〜15:で、シミュレーション結果の表示と、再試行のフォーム、フッタを書いて終了です。

topへ

--------------------------------------------------------------------------------

ストック機の役抽選


1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:

$rep   = 2245;  # 2245/16384 = 1/7.298
$reprt = 14316;

while (1) {
    $g++; $count++;
    if ($coin < 3) { $coin += 50; $k++; $totalk++; }
    $coin -= 3;

    $r = int(rand(16384)+1);
    $repr = ($stock) ? $reprt : $rep;

    if($r <= repr) {  # リプレイ当選
        if (!$stock) {
            $coin += 3; next;
        } else {
            $r = int(rand(8192)+1);
            if ($r <= 1294) { $coin += 3; next; }
        }
    } elsif( … ) {
        …
    }
}

パチスロで乱数の数が 16384 の場合、そのうち 2245 がリプレイとして振り分けられるのが一般的です。

しかし、ストック機が登場してからちょっと変わりました。

ストック機でボーナスが成立すると、リプレイタイム( RT ) というものに突入します。
RT中は 16384 の乱数のうち、小役とボーナスを除いたほぼ全ての数が内部リプレイとして振り分けられます。
そして、内部リプレイを「揃うリプレイ」「揃わないリプレイ」として振り分けるのです。

上のソース例を見てみます。
ボーナスをストックすると $stock=1; とし、10:により、
$stock が真ならば $repr=$reprt; 、$stock が偽ならば $repr=$rep; と代入して、役抽選をしていきます。

RT中で内部リプレイに当選した場合は、さらに 8192 の乱数のうち、 1294 を揃うリプレイに振り分けています。

topへ

--------------------------------------------------------------------------------

初期ストックがある場合


1:
2:
3:
4:

for (1..$s_big) { push(@bonus,"B"); }
for (1..$s_reg) { push(@bonus,"R"); }
while (@bonus) { push(@new_bonus, splice(@bonus,rand(@bonus),1)); }
@bonus = @new_bonus;

ストックしたボーナスをその順番通りに放出する、キングパルサーのようなストック機の場合です。

内部BIG が成立した場合、@bonus という配列の末尾に 'B' を加えます( 内部REG の場合は 'R' )。
そして、RTが解除された場合に $bonus[0] の文字が 'B' ならBIG、'R' ならREG を放出し、
shift(@bonus) により、 @bonus の先頭の要素である $bonus[0] を削除します。

これでキングパルサーのようにストック順にボーナスを放出するシステムはできました。


んで、初期ストックを指定された場合( $s_big=4 , $s_reg=2 。ストック … BIG 4 , REG 2 )
とりあえず @bonus という配列に ストックされたBIG分だけ 'B' という文字を突っ込みます。REGにおいても同様です。
1:〜2:より、 @bonus = ('B','B','B','B','R','R'); となります。

しかしこのままだと、BIG の連荘の後に REG の連荘 ということになり不自然です。
なので、3:により @bonus の中身をランダムにかき混ぜることにしてます。

3:の中身を詳しく見てみます。
while (@bonus) { push(@new_bonus, splice(@bonus,rand(@bonus),1)); }
while (@bonus) {  }
より、ループが永久に続きそうですが、 splice は配列から要素を削除する関数です。
削除した要素を @new_bonus に push関数でぶち込み、次のループに進むわけですので、
いずれ @bonus の要素は全て削除されて 偽となり、while ループから抜けることができます。

rand 関数に配列を指定すると、配列の要素数からランダムな数を選びます。

つまり、最初のループでは rand(6), 2回目のループでは rand(5) …。

splice(A, B, C) → 配列A の B の要素から C個だけ削除

例) @hoge = (0,1,2,3,4,5);  splice(@hoge, 1, 3);
とすると、1,2,3 の要素が削除されて、 @hoge = (0,4,5); となります。


topへ

--------------------------------------------------------------------------------

RTプレイ数の抽選


1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:

@tbl = (11,28,34,30,16,12,20,13,8,13,0,20,14,11,13,13);   # /256
@blk = (0,8,16,24,32,40,48,64,80,96,127,128,256,512,768,1024,1279); # プレイ数振り分け

$rt = &rt;

sub rt {
    my($i,$x,$r,$rt);
    $i = 0; $x = $tbl[0];
    $r = int(rand(256)+1);
    while ($r > $x) {
        $i++;
        $x += $tbl[$i];
    }
    $rt = &rtset($blk[$i], $blk[$i+1]);
    return $rt;
}
sub rtset { # 引数2つの間のランダムな整数を返す
    local($y,$r,$a,$b);
    ($a,$b) = @_;
    $y = $b - $a;
    $r = int(rand($y)+1) + $a;
    return $r;
}

この例は、キングパルサー設定1 のストック有り時のデータです。
最近の機種はストック機ばかりなのでサブルーチン化して使っています。

1:に @tbl として 振り分けのデータを記しておきます。当然これら全てを足すと 256 になります。
2:の @blk は、RTプレイ数の振り分けです。1〜8P, 9〜16P,17〜24P … というやつです。

具体的にサブルーチン rt の中身を見てみます。

8:より、$i=0; $x = $tbl[0] より、 $x=11; となる。
9:で 256 からランダムな数値を選択。ここでは仮に 150 が選ばれたとします。

10:の while の条件式 $r>$x , つまり 200>11
これは真なので 11:〜12:が行われ $i=1; $x = 11+28 = 39; となります。

このループを繰り返し、 $i=6; $x=151; となった時に 条件式を満たさなくなり、 while から抜けます。

$blk[$i] と $blk[$i+1] 、$blk[6]=48; $blk[7]=64; 。
(48,64) という値を引数として サブルーチン rtset に渡し 、49〜64 の間のランダムな数を $rt として返します。


topへ

--------------------------------------------------------------------------------

桁揃えと四捨五入


1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:

$hoge = &point($hoge,3);

sub point {
    my($value, $fig, $x);
    ($value, $fig) = @_;

    $value = int($value * (10 ** $fig) + 0.5) / (10 ** $fig);
    if ($value =~ /(\d+)\.(\d+)/) { $x = $2; }
    elsif ($value !~ /\./)        { $x =''; $value .= '.'; }
    $value = $value.'0' x ( $fig - length($x) );
    return $value;
}


ぐごごごごご。
$y = sprintf("%.3f", $x);
これで一発でした。終了〜。
( 2004/12/8 追記 )


point サブルーチンに 引数として変数と数値を渡せば、 変数を 数値+1 の桁で四捨五入して、[数値] の桁として返します。

例) &point($hoge, 3)
$hoge = 123.4567 → 123.457
$hoge = 123.1234 → 123.123
$hoge = 123    → 123.000

まず、変数に 10^$fig をかけて +0.5 した上で整数化し、 10^$fig で割ってやります。
これで指定された 桁+1 での四捨五入が完了です。

変数に . ( ピリオド ) がマッチするなら、小数点以下の部分を $x に代入。
マッチしないなら、. ( ピリオド) を文字列として追加し、$x には空文字を代入。
そして足りない桁数分だけ 0 を追加します。

topへ




[ home / cgi / juggler / column / diary / bbs / link / welcome ]