競馬であそぶ 5

夏競馬は、なんで難しいの?




7.  スクリプトを改造(5)  

Perl/Tk 版の改造(最終回)

オッズに関しては、人によって考え方が違うので一つの例だけ取り上げてコレがベストとか正しいとか
言う気はありません。
まぁ自分の場合、単勝は別として三連複、三連単、馬単、馬連の順にオッズを見てるかな、という感じ
です。
三連単なんかは工夫のしがいがありそうで、自分のスタイルに合わせて、なんとか見やすいのを作り
たいなと思っています。

今回は、三連単を取り上げます。前回と重複する部分もあります。
説明予定だったpush_go は、名前が変ったりしています。

何時何分時点でのオッズか?どのくらい売れているのか?という情報も JV データには含まれている
ので表示できるようにしてみました。
自分にとって三連複は BOX と2頭軸で充分、三連単は FORMATION かなという気分なので、その方
法でオッズを表示させます。

##### オッズ関連で追加の変数・配列
my $s1 ;    # 三連複馬番選択 Window
my @cb ;    # 三連複馬番用〜ボタン配列
my @odds_tan = () ;
#---------------------------------
my @odds_3rn = () ;
my $odds_inf ;
#---------------------------------
my $s2 ;    # 三連単馬番選択 Window
my $fr1 ;   # フレーム
my $fr2 ;   # 
my $fr3 ;   # 
my @cba ;   # 三連単◎馬番用〜ボタン配列
my @cbb ;   # 三連単○馬番用〜ボタン配列
my @cbc ;   # 三連単▲馬番用〜ボタン配列
#---------------------------------

オッズ部分での、グローバル変数はこれくらいです。

sub sr2_window {    # 三連単
    if ( Exists( $s2 ) ) {
        $fr1->destroy() ;
        $fr2->destroy() ;
        $fr3->destroy() ;
    } else  {
        $s2 = $mw->Toplevel( -title => '三連単' ) ;
        $s2->Label( -text => '三連単 FORMATION',
             -foreground => "#800000",
             -background => "#f0f0d0",
             -font => [ "FixedSys", 14 ] )->pack() ;
        $s2->Button( -text => "Go!", -command => \&push_go2 )->pack() ;
    }
    ( @cba, @cbb, @cbc ) = () ;
    &get_odds() ;
    my ( $tosu ) = @{$odds_inf}[2] ;
    $fr1 = $s2->Frame() ;
    $fr2 = $s2->Frame() ;
    $fr3 = $s2->Frame() ;
    for my $i ( 1..$tosu ) {    # 馬番号のついたボタン◎用
        my $j = sprintf "%2d", $i ;
        $cba[$i] = $fr1->Checkbutton( -text => $j,
                                    -indicatoron => 'no',
                                    -selectcolor => "#ffffb0"
        )->pack( -side => 'left' ) ;
    }
    $fr1->pack( -anchor => 'c' ) ;
    for my $i ( 1..$tosu ) {    # 馬番号のついたボタン○用
        my $j = sprintf "%2d", $i ;
        $cbb[$i] = $fr2->Checkbutton( -text => $j,
                                    -indicatoron => 'no',
                                    -selectcolor => "#ffebb0"
        )->pack( -side => 'left' ) ;
    }
    $fr2->pack( -anchor => 'c' ) ;
    for my $i ( 1..$tosu ) {    # 馬番号のついたボタン▲用
        my $j = sprintf "%2d", $i ;
        $cbc[$i] = $fr3->Checkbutton( -text => $j,
                                    -indicatoron => 'no',
                                    -selectcolor => "#ffeef0"
        )->pack( -side => 'left' ) ;
    }
    $fr3->pack( -anchor => 'c' ) ;
}

頭数はこんなふうにしてみました。 my ( $tosu ) = @{$odds_inf}[2] ;
$odds_inf は、配列のリファレンスです。

頭数分のボタンを3列並べただけで素っ気ないですが、とりあえずこれで我慢。
Frame を使いましたが、このへんは分かって使ってるわけではなく、山勘です。

sub push_go2 {
    my @se1 ;
    my @se2 ;
    my @se3 ;
    my ( $kubn, $jifun, $tosu, $hyo ) = @$odds_inf ;
    $hyo += 0 ;
    my ( $ji, $fun ) = unpack 'A2A2', $jifun ;
    my $jikan = $ji . '時' . $fun . '分' ;
    my $bln = $kubn == 1 ? $jikan : $kubn == 2 ? '前売最終' :
              $kubn == 3 ? '最終' : $kubn == 4 ? '確定' :
              $kubn == 5 ? '確定(月)' : '--------' ;
    for my $i ( 1..$tosu ) {
        ${ $cba[$i]->cget( -variable ) } and push @se1, $i ;
        ${ $cbb[$i]->cget( -variable ) } and push @se2, $i ;
        ${ $cbc[$i]->cget( -variable ) } and push @se3, $i ;
    }
    print "\n\t 三連単〜FORMATION〜 $bln オッズ (人気) \t売計 ${hyo}万円\n\n" ;
    for my $i ( 0..$#se1 ) {
        my $aa = $se1[$i] ;
        for my $j ( 0..$#se2 ) {
            my $bb = $se2[$j] ;
            next if $aa == $bb ;
            for my $k ( 0..$#se3 ) {
                my $cc = $se3[$k] ;
                next if $aa == $cc ;
                next if $bb == $cc ;
                my ( $odds, $jun ) = unpack 'A7A4', $odds_3rn[$aa][$bb][$cc] ;
                $odds /= 10 ;
                $jun = sprintf "(%4d)", $jun ;
                $odds = $odds == 0 ? '     ---' :sprintf "%9.1f", $odds ;
                my $deme = sprintf "\t\t [%2d→%2d→%2d]", $aa, $bb, $cc ;
                print "$deme $odds $jun\n" ;
            }
        }
    }
}

これでいいのなら、出力に関しては三連複より簡単です。
三連複用のpush_go 改め ⇒ push_go1 は直接スクリプトを見てく ださい。

オッズ取得(三連単の場合)の部分は以下の通りです。

    if( $rad == 4 ) {       # 三連単オッズ
        my $odds_6 = "${zpass}/${yymmdd}/0B36${yymmdd}${bbrr}\.dat" ;
        open DAT, $odds_6 if -e $odds_6 ;
        while ( <DAT> ) {
            $do1->() ;
            my ( $kubn, $jifun, $to, $alls, $hyo ) = unpack
                '@2 A1 @31 A4 @35 A2 @40 a83232 @83272 a9', $_ ;
            $odds_inf = [ $kubn, $jifun, $to, $hyo ] ;
            my @ozdat = unpack 'A17' x 4896, $alls ;
            for ( @ozdat ) {        # 三連単ODDS
                my ( $z1, $z2, $z3, $zoz ) = unpack 'A2A2A2A11', $_ ;
            $odds_3rn[$z1][$z2][$z3] = $zoz ;
            }   
        }
        close DAT ;
    }

$hyo は本来、ここでは11桁(100円単位)ですが、上から9桁(万単位)取ってきています。
このように、JV データ仕様書のフォーマットをコチラの都合で刻んでいます。
$jifun も本来、前に月日が4桁入っています。


他は、自分で改造して出力してみてください。


ココまでの修正を含めた最終版 をUPしておきます。(2005.08.08)

⇒⇒ 0022〜0023 はコチラから


★ 早速、修正です。レース前日などで、まだオッズが無い時点で三連複オッズを 見ようとすると
エラーが表示されます。別に実害はないのですが気になる人は対策してください。 (2005.06.24)

get_odds の三連複オッズ取得部分で
        next unless -e $odds_5 ;
        open DAT, $odds_5 ;
の2行を
        open DAT, $odds_5 if -e $odds_5 ;
の1行に書き換えます。三連単のほうは既に修正済みです。

★★ 更に修正です。こちらは、重要な修正です。
グレードレースがすべてオープンと表示され、オープン特別として扱われていました。 (2005.06.26)
kbs の最初の部分で
    my $gr = substr( $kline, 505, 2 ) ;

    my $gr = substr( $kline, 505, 1 ) ;
と書き換えます。
この調子だとマダ不安ですが、修正はこちらに追加で書き込む予定でいます。


Tk の楽しさは、Widgets の知識を深めながら、色、形、大きさ、位置などを好きなように並べ替えるだけ
でなく使いやすいようにイクラでも改造できることだと思います。
その領域まで深く踏み込む気がなくても結構、簡単に出来そうな気もします。

今回は、改造のために必要と思われる部分を重点的に取り上げてきました。
Perl について少しでも興味や関心のある人なら、これで充分だと思います。
JV データの部分以外については、そのまま使わなくても参考になるとおもいます。
シェープアップの余地もいっぱいあるし、Perl は一つのことをやるにも 100 の方法がある ナンテことを
いわれるほど奔放な言語です。楽しんでください。


ここまで、モジュールから大きく外れて、Perl/Tk 版への改造に終始してしまいました。
しかし、JV データを手軽にプログラミングする手段として、Rerl/Tk はかなり有力 だと思います。
ソフトとして売るのが目的なら勧めませんが馬券で勝つためのツールとして使うのなら、ここにも
サンプルがあることだし、最高の選択肢じゃないでしょうか?



いいのが出来たら公開するか、教えてください。
気に入ったら、それを使わせていただくかもしれませんので、、








8.  Win32::OLE (失敗例)  

Perl から JV-Link をナントカしようと思って、取り組んだのがこのモジュールです。
実のところ、うまくいきませんでした。だから、役に立つ情報ではありません。
どうしてもコレがなければダメという訳ではないので、次に挑戦するのがいつになるかは
分かりませんが、その時 or 他の勇者・賢者のために経過をメモしておきます。

先ず、JV-Link 用のサブルーチンから、

sub jv_first {
    my $jvd ;
    eval { $jvd = Win32::OLE->GetActiveObject( 'JVDTLab.JVLink' ) } ;
    die "JVLink not installed" if $@ ;
    unless (defined $jvd) {
        $jvd = Win32::OLE->new( 'JVDTLab.JVLink', sub { $_[0]->Quit ; } )
             or die "Cannot start JVLink .. 残念〜" ;
    }
    return $jvd ;
}

この部分は、OLE Automation への定型です。

sub jv_init {       # JVLink 初期化
    my $sid = 'UNKNOWN' ;
    my $rtn = $jvd->JVInit( $sid ) ;
    if( $rtn == 0 ) {
        print "初期化は正常です。\n" ;
        my $version = $jvd->m_JVLinkVersion ;
        print "JVLink Version; $version \n" ;
    } else {
        die "適切な sid ではありません。$rtn\n" ;
    }
    return $rtn ;
}

これは、JVLink の初期化です。

sub jv_set {        # 設定変更
    my $rtn = $jvd->JVSetUIProperties() ;
    print "JV-Linkの設定に失敗しました。$rtn\n" unless $rtn == 0 ;
    return $rtn ;
}

JVLink の設定変更のダイアログを表示させて、設定をレジストリに保存します。

sub jv_close {          # JVClose
    my $rtn = $jvd->JVClose() ;
    print "JVClose エラー。$rtn\n" if $rtn == -1 ;
}

JVLink を閉じます。

以上のサブルーチンを使って、スクリプトを書いてみます。

#!/usr/bin/perl

use strict ;
use Win32::OLE ;
# use Config::Simple ;
# my $cfg = new Config::Simple( 'JVSET.ini' ) ;   # 設定ファイル

my $jvd = &jv_first() ;
&jv_init() ;
&jv_set() ;
&jv_close() ;

ここまでは、問題ないようです。
Config に関する2行は関係ないものとしてコメントアウトしています。

以後、使う可能性のあるサブルーチンを列挙します。

sub jv_open {       # JVOpen
    my $rtn = $jvd->JVOpen( @_ ) ;
    return $rtn ;
}

sub jv_rtopen {     # JVRTOpen
    my ( $ds, $ky ) = @_ ;
    my $rtn = $jvd->JVRTOpen( $ds, $ky ) ;
#   &respo( $rtn ) ;
    return $rtn ;
}

sub jv_status {     # JVStatus ダウンロード完了数 or 失敗(マイナス数)
    my $rtn = $jvd->JVStatus() ;
    return $rtn ;
}

sub jv_read {       # JVRead
    my ( $buff, $bsize ) = @_ ;
    my $fn ;    # ファイル名
    my $rtn = $jvd->JVRead( $buff, $bsize, $fn ) ;
    return $rtn ;
}

sub jv_gets {           # JVGets
    my ( $buff, $bsize ) = @_ ;
    my $fn ;    # ファイル名
    my $rtn = $jvd->JVGets( $buff, $bsize, $fn ) ;
    return $rtn ;
}

sub jv_skip {           # JVSkip
    $jvd->JVSkip() ;
}

sub jv_cancel {     # JVCancel
    $jvd->JVCancel() ;
}

sub jv_delete {         # JVFileDelete
    my $fn = shift ;
    my $rtn = $jvd->JVFileDelete( $fn ) ;
    print "削除エラー。$rtn\n" if $rtn == -1 ;
    return $rtn ;
}

######################################################################
sub get_ymd {
    my $yymmdd ;
    print  "
             〜 開催日を指定 〜
            ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
            ┃ レースの年月日を yymmdd 形式(6桁)で入力してください。 ┃
            ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
    \n\n\t\t ==> " ;
    chomp( $yymmdd = <STDIN> ) ;
    $yymmdd += 20000000 if $yymmdd < 500000 ;
    return $yymmdd ;
}

sub get_rnum {
    my $rnum ;
    print  "
             〜 レースNo.を指定 〜
                    (例) 11R だけなら 11、1R〜12R なら 112
            ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
            ┃ 上の例を参照。レースNo.を1〜4桁 で入力してください。 ┃
            ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
    \n\n\t\t ==> " ;
    chomp( $rnum = <STDIN> ) ;
    return $rnum ;
}

試しにデータマイニングのデータを取得してみます。
以下がそのための、サブルーチンです。
$dir は、Config用の iniファイルから取得していますが、直接指定することも出来ます。
まぁ出力する場所だから、どこでもいいので、'' にしておきます。

sub get_dmining {
    my $ds = '0B13' ;
    my $rtn = $jvd->JVRTOpen( $ds, $yymmdd ) ;
    if( $rtn == 0 ) {
#       my $dir = $cfg->param("pass.yobiP") ;   # 直接指定もOK
        my $dir = '' ;
        my $bsize = 1024 ;
        my $buff = "\x00" x $bsize ;
        my $fn = '' ;   # ファイル名
        my $param = {
            buff => $buff,
            size => $bsize,
            filename => $fn } ;
        my $flg = 1 ;
        my $i = 0 ;
        my @dat = () ;
        while ( $flg ) {
            $flg = $jvd->JVRead( $param ) ;

print "フラグは  $flg ファイルネームは $param->{filename} $param->{size}\n" ;
            while ( $jvd->Busy ) {
                sleep 1 ;
            }
            last if $flg == 0 ;
            if( $flg > 0 ) {
                $buff =~ s/\x00+$// ;
                $dat[$i] = $buff ;
                $i++ ;
            } elsif( $flg == -1 ) {
                my $fname = $dir . $fn . '.dat' ;
                open DAT, ">$fname" ;
                for( @dat ) {
                    print DAT $_, "\n" ;
                }
                close DAT ;
            } else {
                print " 読み込みエラー。 $flg\n" ;
            }
        }
    } elsif( $rtn == -1 ) {
        print "該当データがありません。$rtn\n" ;
    } else {
        print "OPENエラー。$rtn\n" ;
    }
    jv_close() ;
}

このサブルーチンは失敗します。
JVRead の戻り値は、ファイルサイズです。これは、303の正しい数値を返しています。
但し、引数は[out][out][out]なのに、返してきた筈のデータを受けた形跡がありません。
どうやら、引数が[in]の場合は、(例えばJVRTOpen は[in][in]) 問題ないのですが、JV の
関数が引数に値を返してくる時に失敗しているようです。

引数に[out] があるのは、JVOpen と JVRead と JVGets の三つだけなので、ナントカ
したいという気はあるのですが、、力不足は如何ともしがたい。

対策として、XS なんかを使うのか?とか考えたのですが、やはり自然に分かるまで放置
という、いつも通りの結論になりました。
再度、一からやり直すのもナンなので、失敗事例をここに残しておきます。

と思ったのですが、上記については Win32::OLE::Variant を使うことで解決 しました。
他にも解法があるのかもしれませんが、まぁスッキリしました。

これで、なにもかも出来るようになれば言うことはありませんが、暫らくこの玩具で遊ん
でから海の日頃迄には公開したいと思います。
「競馬であそぶ 5」 もうまい具合に2項、残っているのでそこで、、


INDEXへ トップページへ 次へ