鳥の巣箱

ネトゲしたり、機械いじったり、ソフト書いたり、山篭ったり、ギャンブルしたりする人

RIFFフォーマットファイルの読み込み

DelphiでWaveファイルを扱うためにRIFFフォーマットのファイルを読み込む必要がでてきたんで色々とメモ書き。


まず必要なものの説明。

MMSystemライブラリ

マルチメディア関連のAPIが各種はいっている。
uses句にこれを追加する必要あり。

MMRESULT型

MMSystem内の関数は一部、戻り値をこの型で返してくる。
中身はエラーコード。

TWaveFormatEx構造体

Wave形式のオーディオデータフォーマットが定義されている構造体。
この構造体のメンバは

メンバ名 データ型 詳細                     
wFromatTag WORD ウェーブフォームオーディオフォーマットのタイプ 
nChannel WORD チャンネル数、モノラルなら1Ch、ステレオなら2Ch
nSamplesPerSec DWORD サンプリング周波数、標本化周波数とも
nAvgBytesPerSec DWORD フォーマットタグで必要な平均データ転送速度
nBlockAlign WORD ブロックアライメント、フォーマットタイプのデータの最小単位
wBitesPerSample WORD 1サンプリングあたりのビット数
cbSize WORD WaveFormatEx構造体の後ろに追加されるフォーマット情報のサイズ

hMMIO型

オープンされているファイルのハンドルを格納するためのもの。

TMMCKINFO構造体

RIFFファイル内のチャンクに関する情報が定義されている。
TMMCKINFO構造体のメンバは

メンバ名 概要
ckid FOURCC チャンク識別子
cksize DWORD チャンクのサイズ
fccType FOURCC フォームのタイプ
dwDataOffset DWORD Discend、Ascendを行ったあとのファイル上での位置
dwFlags DWORD 原則0が格納

mmioOpen関数

入出力をバッファリングしてファイルを開くための関数。
パラメーターは全部で3つ。

mmioOpen(szFilename,lpmmioinfo,dwOpenFlags);
パラメータ データ型 概要
szFilename LPSTR 開くファイルのファイル名がはいった文字列のアドレスを指定
lpmmioinfo LPMMIOINFO ファイルを開くためにインストールされていないI/Oプロシージャを指定する場合以外はnil指定
dwOpenGlags DWORD オープン操作のためのフラグを指定
MMIO_ALLOCBUF : ファイルを不バッファリングされた入出力用に開く
MMIO_COMPAT : ファイルを互換モードで開き、指定されたマシン上の全てのプロセスでそのファイルを何度も開くことができるようにする
MMIO_CREATE : 新規ファイルを作成する
MMIO_DELETE : ファイルを削除する
MMIO_DENYONE : 他のプロセスからのファイルの読み書きを拒否せず開く
MMIO_DENYWRITE : ファイルに対する他のプロセスからの読み取りを拒否して開く
MMIO_EXCLUSIVE : ファイルに対する他のプロセスからの読み書きアクセスを拒否して開く
MMIO_EXIST : 指定されたファイルが存在するかどうかを調べ、szFilenameパラメータで指定したパスからファイル名を作成
MMIO_GETTEMP : szFilenameパラメータに渡されるパラメータを暫定的に使いテンポラリファイル名を作成
MMIO_PARSE : MMIO_EXISTに近いが存在するかどうかを調べない
MMIO_READ : ファイルを読み取り専用として開く
MMIO_READWRITE : ファイルを読み書き用に開く
MMIO_WRITE : ファイルを書き込み専用として開く

関数が成功すると、開いたファイルのハンドルが返ってくる。

mmioStringToFOURCC関数

NULLで終わる文字列を4文字コードに変換する。
第1引数に変換したい文字列、第2引数はフラグを指定するが「MMIO_TOUPPER」の1種のみ。
フラグを指定するとすべての文字が大文字に変換される。変換が必要ない場合、パラメータは0でよい。

mmioAscend関数

mmioDescend関数で侵入、またはmmioCreateChunk関数で作成したRIFFファイルのチャンクから退出する関数。

mmioAscend(hmmio,lpck,wFlags);
パラメータ データ型 概要
hmmio HMMIO 開いてるRIFFファイルのハンドルを指定
lpck LPMMCKINFO mmioDescend関数またはmmioCreateChunk関数で値が書き込まれている、アプリケーション定義のMMCKINFO構造体のアドレスを指定
wFlags UNIT 予約されているので0を指定

関数が成功するとMMSYSERR_NOERRORが返る。

mmioDescend関数

mmioOpen関数で開いたRIFFファイルのチャンクへ進入する。または指定されたチャンクを検索する。

mmioDescend(hmmio,lpck,lpckParent,wFlags);
パラメータ データ型 概要
hmmio HMMIO 開いてるRIFFファイルのハンドルを指定
lpck LPMMCKINFO アプリケーション定義のMMCKINFO構造体のアドレスを指定
lpckParent LPMMCKINFO 検索するチャンクの親を識別するためのMMCKINFO構造体のアドレスを指定
親チャンクが指定されてない場合nil指定
wFlags UNIT 検索フラグを指定する
MMIO_FINDCHUNK : 指定されたチャンク識別子のチャンクを検索
MMIO_FINDLIST : チャンク識別子がLISTで指定されたフォームタイプのチャンクを検索
MMIO_FINDRIFF : チャンク識別子がRIFFで指定されたフォームタイプのチャンクを検索

関数が成功するとMMSYSERR_NOERRORが返る。

mmioRead関数

mmioOpen関数で開いたファイルから指定されたバイト数を読み取る。

mmioRead(hmmio,pch,cch);
パラメータ データ型 概要
hmmio HMMIO 読み取るファイルのハンドルを指定
pch HPSTR ファイルから読み取られたデータが入るバッファのアドレスを指定
cch LONG ファイルから読み取るバイト数を指定

と、この辺を使えば読み込みが可能になる。
関数などのパラメータ等はかなり簡略化してまとめてある。
詳細はMSDNなどで関数名を調べれば出てくる。C系の書き方がしてあるが、書式を変えればそのまんまDelphiでも使える。
今書いてるソースの中から該当部分だけをピックアップするとこんな具合。

uses MMSystem;

//==============================//
//    FileOpenDialog呼び出し    //
//==============================//
procedure TForm1.BtOpenClick(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    wav_open(OpenDialog1.FileName);
  end;
end;

//========================================//
//    waveファイル読み込みプロシージャ    //
//========================================//
procedure TForm1.wav_open(name:string);
var
  x           : integer;
  mmres       : MMRESULT;
  PCM         : TWaveFormatEx;
  hIO         : hMMIO;
  RIFF_INFO   : TMMCKINFO;
  DATA_INFO   : TMMCKINFO;
  FMT_INFO    : TMMCKINFO;
  buf         : array[0 .. (1024 -1)] of Smallint;
  sz          : DWORD;
  remain      : DWORD;
  read_size   : DWORD;
  total_size  : DWORD;
  limit       : DWORD;
  WaveData1   : array of Smallint;
  WaveData2   : array of Smallint;

begin
  Ch1Index := 0;
  Ch2Index := 0;

  //  wavファイル読み込み
  hIO := mmioOpen(PWideChar(name),nil,MMIO_READ);
  try
    RIFF_INFO.fccType := mmioStringToFOURCC('WAVE',0);
    mmres := mmioDescend(hIO, @RIFF_INFO, nil, MMIO_FINDRIFF);
    if(mmres <> MMSYSERR_NOERROR) then
    begin
      Memo1.Lines.Add('ERROR');
      Exit;
    end;

    FMT_INFO.ckid := mmioStringTOFOURCC('fmt', 0);
    mmres := mmioDescend(hIO, @FMT_INFO, @RIFF_INFO, MMIO_FINDCHUNK);
    if(mmres <> MMSYSERR_NOERROR) then
    begin
      Memo1.Lines.Add('FMT CHUNK ERROR');
      Exit;
    end;

    sz := mmioRead(hIO, @PCM, FMT_INFO.cksize);
    if sz <> FMT_INFO.cksize then
    begin
      Memo1.Lines.Add('Wave Format ERROR');
      Exit;
    end;

    DATA_INFO.ckid := mmioStringTOFOURCC('data',0);
    mmres := mmioDescend(hIO, @DATA_INFO, @RIFF_INFO, MMIO_FINDCHUNK);
    if (mmres <> MMSYSERR_NOERROR) then
    begin
      Memo1.Lines.Add('Data Chunk Error');
      exit;
    end;

    remain := DATA_INFO.cksize;     // waveファイルのデータ数
    read_size := Length(buf) * sizeof(Smallint);
    total_size := 0;
    
    //  読み込んだデータを格納するための配列の長さを決定
    //  2chあるためデータ総数div2
    SetLength(WaveData1,DATA_INFO.cksize div 2);
    SetLength(WaveData2,DATA_INFO.cksize div 2);

    while remain > 0 do
    begin
      if read_size > remain then
        read_size := remain;

        mmioRead(hIO, @buf, read_size);
        for x := 0 to (read_size div (sizeof(Smallint))) - 1 do
        begin
          if (x mod 2) = 0 then
            begin
              WaveData1[Ch1Index] := buf[x];
              inc(Ch1Index);
            end
          else
            begin
              WaveData2[Ch2Index] := buf[x];
              inc(Ch2Index);
            end;
        end;

        inc(total_size, read_size);
        dec(remain, read_size);

        if limit > 0 then
        begin
          if total_size >= limit then
            Break;
        end;
    end;

    mmioAscend(hIO, @DATA_INFO, 0);

    mmioAscend(hIO, @RIFF_INFO, 0);
  finally
    mmioClose(hIO, 0);
  end;
end;

ざっくり流れとしては
1.ファイル展開
2.チャンクを検索してWaveファイルかどうか調べる
3.チャンクを検索してフォーマットが正しいか調べる
3.チャンクを検索してデータフォーマットが正しいか調べる
4.配列の長さをデータ数に合わせる
5.チャンネルごとにデータを分けて格納していく
6.チャンクから退出
7.ファイルを閉じる

という感じ。
これだとWaveファイルをただ開いてデータを取り込むだけなので、追加で色々実装する必要はあり。
TWaveFormatEx構造体のメンバは結構良く使う。

TImageのリサイズ時に気をつけること

Delphiやりはじめて初日ですどうも。早速躓きまくってるので色々メモ書きをガンガン残していきます。




DelphiでTImageコンポーネントを、フォームのサイズに合わせてリサイズしようとしていたのだが、どうにもうまくリサイズされない。

通常のコンポーネントは、プロパティのAlignとAnchorsを設定すればフォームの大きさに合わせて可変する。

しかし、TImageさんはフォーム起動時の大きさを維持したまま動かない。
なぜ??

最初に書いたソースの該当部分はこれ。

procedure TForm1.FormResize(Sender: TObject);
var
  //X軸Y軸開始座標格納
  XAxis, YAxis : Integer;

begin
  XAxis := 0;
  YAXis := 0;

  //キャンバスの各種描画
  with Image1.Canvas do
  begin
    //キャンバスの塗りつぶし
    brush.Color := clblack;
    fillrect(Image1.Canvas.ClipRect);

    //軸の描画
    Pen.Color := clLime;
    pen.Width := 3;
    XAxis := Image1.Height div 2;
    moveto(0,XAxis);
    lineto(Image1.Width,XAxis);
  end;

フォームのリサイズイベントハンドラにImage1.Canvasの塗りつぶしと描画を発生させている。

しかし、フォームを可変させようとコンポーネントのサイズ自体が変化しないのであればなんの意味もない。
なんでやろかと調べてたところ、TImageのCanvasプロパティは、他のコンポーネントのそれとはちょいと違うらしい。

TImageのCanvasプロパティを利用して描画をしてから表示されるまでの流れは
1.Canvasプロパティを使って描画を行おうとすると、PictureGraphicに代入されてる「TBitmap」に描画がはじまる
2.TBitmapでOnChangeイベント発生 → TImageのイベントハンドラ呼び出し
3.イベントハンドラでTImageの表示領域をInvalidateメソッドで無効化し、Windowsに再描画を要求
4.Updateメソッドで強制的に再描画。しかしWindowsからの再描画要求によってPaintメソッドが呼ばれ、Picture.Graphicに代入されているTBitmapがTImageの表示面にStretchDrawで描画される。

というメンドクサイ手順を追ってるようだ。
もともとTImageというものがPicture.Graphicに代入されている画像を表示するコンポーネントらしい。
暗黙的にTImageはTBitmapを抱えているようだ。

抱き合わせ販売とか詐欺まがいなことはやめてほしい()

つまりTBitmapのサイズをフォームに合わせてリサイズしてやり、そのサイズにあわせてTImageをリサイズさせる。
という段取りを経る必要があるようだ。

よって、上記のソースはこうなる。

procedure TForm1.FormResize(Sender: TObject);
var
  //Image_AというTbitMapダミーのコンポーネントを宣言
  //実際には表示されない
  Image_A : TBitMap;

  //X軸Y軸開始座標格納
  XAxis, YAxis : Integer;

begin
  XAxis := 0;
  YAXis := 0;

  //Image_Aを生成
  Image_A := TBitMap.Create;
  //Image_AのWidthとHeightをForm1に合わせる
  Image_A.Height := Form1.Height;
  Image_A.Width := Form1.Width;
  //Image1をImage_Aにあわせる
  Image1.Picture.Bitmap := Image_A;
  //オブジェクトの解放
  Image_A.Free;
  Image_A := nil;

  //キャンバスの各種描画
  with Image1.Canvas do
  begin
    //キャンバスの塗りつぶし
    brush.Color := clblack;
    fillrect(Image1.Canvas.ClipRect);

    //軸の描画
    Pen.Color := clLime;
    pen.Width := 3;
    XAxis := Image1.Height div 2;
    moveto(0,XAxis);
    lineto(Image1.Width,XAxis);
  end;

end;

とまぁ、これでうまく動く。
注意しなければならないのは

Image_A.Free;
Image_A := nil;

この記述を忘れると、リサイズを繰り返すうちにメモリを食い潰すことになる。(普通にフリーズする)
Image_Aというポインタがリサイズされるたびに生成され続けることになる。
TImageのリサイズが終わったタイミングで必ず開放すること。

DelphiのエディターをMonokai風にアレンジ

仕事の都合上、Delphiデビューすることになりました。

エディターが初期設定だとかなり見づらいので、カラーチェンジの設定をメモ書き程度に書いていきます。

SublimeTextの方でカラースキーマはMonokaiを使ってるので、感覚的にはこれに近づけていく感じでいじりました。
参考までにどうぞ。

要素 前景色 背景色
基本の背景色 39,40,34
ホワイトスペース 銀色 39,40,34
コメント 255,189,123 39,40,34
予約語 102,217,239 39,40,34
識別子 166,226,46 39,40,34
シンボル 白色 39,40,34
文字列 230,219,116 39,40,34
文字 230,219,116 39,40,34
数値 217,130,208 39,40,34
実数 217,130,208 39,40,34
8進数 217,130,208 39,40,34
16進数 217,130,208 39,40,34
アセンブラ 255,128,64 39,40,34
検索一致 黒色 127,170,255
マークされたブロック 黒色 127,170,255
不正なブレークポイント 238,226,204 90,90,90
有効なブレークポイント 黒色 186,186,254
無効なブレークポイント 白色 灰色
実行ポイント 黒色 189,255,189
エラー行 白色 255,108,108
行番号 灰色 39,40,34

このほか細かいところは好みに合わせて調整すればいいかと思います。

Androidの内部メモリ容量がいっぱいな時に見ると少し幸せになれるかもしれない

というわけで。ちょっとタイトルが長いね(


先日携帯を買い換えて、色々と設定やらアプリの導入やらは終わってたけれども
ある一点を確認してなかったなと思い出したのでつらつら書いてみます。

中には困ってる方もいるかもしれませんねぇ。


前のスマホを使ってた時、内部メモリの容量不足に悩まされ続けた時がありまして
最近使わなくなったアプリやら、キャッシュを定期的に掃除したりと涙ぐましい努力をしていたのでございますが
いかんせん全く容量不足が解決してくれない!!

なんでやろなぁ、と思ってたところ「設定」から「ストレージ」を開いて種類ごとの使用量をみてみたところ
画像データで8GBも容量を食っていたという驚愕の事実が判明した!!!

。。。いや、おかしくね。

写真だってそんなに撮ってるわけでもなく、8GB分も画像ファイルを入れているわけがない。
そもそも写真撮っても精々3MB程度のもの。うん、絶対おかしい。

ESファイルエクスプローラーを使って、端末内/sdcard/Pictureの中身を確認。
まずプロパティから容量確認。

8GBある。。。(

中に入っていくつかディレクトリがあるのでそれぞれの容量を確認。

そして気がつく。

中のフォルダ合計しても8GBに到底ならない。。。!!!
600MB程度なんですね。
7.4GBはどこからでてきた(


もしや。。。?と思ってESファイルエクスプローラーの設定を確認。
デフォルトだと隠しファイルの表示がオフになってるのでこれをオンに。

するとPictureの中身に「.thumbnails」というディレクトリが出現した。。。!!
プロパティから見るとビンゴ。7GBほど容量食ってた。ナンダコイツ。


中身は名前の通り各種アプリから読み出すのであろうサムネイルが保存されてたのだが
圧縮もされずそのまんまコピーされたものが、どんどんこの中に追加されていってる模様。
キャッシュクリアでも消えないとかタチ悪い(

というわけで、この中身をクリアして容量のほうは大量に確保できました。


しかし、これも一時的な措置にすぎず、またある程度使っていくうちに中身は増えていくわけです。
これらをクリアしたところで、実は各種アプリに対して影響を及ぼさないわけで。
できれば半永久的にサヨナラしていただきたいところ。

で、どうすればいいのか。
.thumbnailディレクトリが残っている限りは、延々と追加され続けます。
かといって.thumbnailディレクトリを削除しても新たにディレクトリが生成され、追加されていきます。

なので.thumbnailディレクトリを削除した後に、空の.thumbnailというダミーファイルを作っておけばいい。
それだけです。
同名のダミーが存在するので、新たにディレクトリが生成されることもありませんし
そもそもディレクトリではないので新たにキャッシュが追加されることもありません。

幸せになれます。


ちなみに。
以前使ってたのが、Android 4.x系列で、今回が6.x系列です。

4.x系列では前述のPictureディレクトリにいたはず。
6.x系列の方を確認してみたところ、Pictureディレクトリにはなく、DCIMディレクトリにいました。
生成されるのはカメラ撮影したものとスクリーンショットのサムネイルキャッシュです。
これも同様の手順でオサラバできますので、容量不足に悩んだ時は一度確認してみるといいかもしれません。

5.x系列は手元にないので確認はできませんが、おそらく似たような事にはなってるでしょうね。

ぷそ的ギャンブラーの脳内【ブラックニャック編】

ちゃおちゃお。


そういえば「メインクラスはギャンブラー()」とか宣っておきながら、カジノに関することを何一つ書いたことがなかったことに気が付きました。←

PSO2のカジノ、現状4種類ありますがとりあえずそれぞれに関する僕の所見をば。


1.ラッピースロット
比較的勝てる方。当たり台を見つけるのが最初の難関ではある感じ。
見つけられれば勝ち。ただし大きく勝つには長期戦必死。
ながら作業でボタンポチポチしてればいいという点では楽。

2.メセタンシューター
最強に勝てる。負けとはなんだと問いたくなる()
基本さえ抑えればほぼ100%負けることはない。
確実に稼ぎたいならコレ。人数集められれば尚良。
ただし、これに集中する必要がある。他に何もできない()

3.ブラックニャック
基本的には勝てる。だが、ニャウ。貴様は許さん。
ながら作業でもいけないこともないけど、ちょいちょいシンキングタイムが入るのが煩わしい。
運が悪いと負けがこむこともある。全然ある。ニャウ、ちょっと表出ろ()

4.リーリールーレット
まずまずをもって勝てない。
一発当てることはできても、長期的にみると大概マイナスになっていることが多い。
ルーレットにおける勝つための正攻法が使えないため、運否天賦の博打。
己の運試しに数回やるくらいでいいや()


とまぁ、僕の中ではそれぞれこんな立ち位置。
稼ぎやすさとしては
メセタン>スロット≧ニャック>>>越えられない壁>>>ルーレット
って感じですね。


とりあえず僕の中では、ルーレット以外には必勝法。。。ではないですが、負けにくい方法は持ってます。


カジノで稼ぎたい。って人がどれだけいるかは知らないけど()



まぁ、今回はブラックニャックについてちょっと解説していきます。


ブラックニャックとは。。。?

ブラックニャックはいわゆる「ブラックジャック」とほぼおなじルールです。
手札のカードの合計を「21」に近づけていき、ディーラーと自分との勝負になります。

基本のルールはブラックジャックとほぼ一緒なので、基本的な思考もブラックジャックのそれとほぼ同じ考えになります。


ほぼ。。。というのがなんとも言えない、ニャックの運要素を高めている要因でもあるんですね。。。
ブラックジャックそのものは、基本的な考え方と賭け方で高確率で勝てる方法があるわけです。
ニャックでもそれらはある程度通用するのですが、ジャックよりも運要素が強く負ける率も若干高いのです。

ジャックとニャックの相違点は、プレイヤー側の選択肢の違いとスペシャルカードの存在、ディーラーの行動の違いです。
それぞれ説明していきます。


1.プレイヤー側の選択肢の相違
ブラックニャックにはプレイヤーの選択肢として
  ・ヒット(カードを一枚引く)
  ・ステイ(カードを引かずに今の手札で勝負する)
  ・ダブルダウン(最初の選択時のみにできる行動。カードを1枚だけ引き、ベット金額を倍にして勝負する)
という3つの選択肢が用意されていますね。

ですが、本来のブラックジャックにはこれらも当然のことながら
  ・スプリット
  ・インシュランス
  ・サレンダー
といった選択肢もあるのです。それぞれの内容はニャックには直接結びつかないので割愛します。

つまるところブラックニャックはブラックジャックよりも、ルール自体は比較的簡素な設定になっているということです。


2.スペシャルカードの存在
ブラックニャックにはスペシャルカードというものが存在しています。
これを手札に混ぜた状態でディーラーに勝つとボーナス払いが発生します。
ですが、カードの数字はサイコロで決定されます。複数枚のSPカードがあった場合はサイコロの目がその都度加算されていきます。
プレイヤー側のサイコロは0~2の3パターン。ディーラーがサイコロを振る場合だけ特別仕様で0、2、4のサイコロが振られます。

このSPカードの存在が、ニャックの運要素を加速的に増やしています。
勝てればウマイんだけどね()


3.ディーラーの行動
ブラックジャックでは、ディーラーは自分の手札の数字が17になるまでは必ずヒットする。というルールがあります。
ニャックにおいても同様です。17を超えた時点でヒットはしなくなります。
。。。が、ぐう畜ニャウは稀に「もう一枚引くニャウ!!!」とかなんとかほざき、ヒットする時があるわけです。
何度それで煮え湯を飲まされたことか(
こういったランダムなイベントもニャックの運要素が高い理由の一つです。


つまるところ、ルールは単純だが運要素がちょっと強いブラックジャック
これがブラックニャックです。(僕の中での

基本的な考え方(ベーシックストラテジー

さて、それではニャックで勝つための基本的な考え方を解説していきましょう。

カード内容は単純にトランプ52枚+SPカードの構成。
2~10は単純に数字分のポイントですが、
Aは1か11(任意に選べる)
J、Q、Kは10として扱われます。

つまるところ、Aは特別として「10、J、Q、Kという全体の約1/3のカードが10として扱われる」というこの点が非常に重要なポイントになります。
ヒットしたカードは1/3の確率で10がきます。

これはヒットするカードに限らない話です。
必ず最初に手札として2枚のカードが配られますが、片方一枚は伏せられていますね。
この伏せられたカードも1/3の確率で10が眠ってると見れます。

応用的思考です。
例えば。。。
  ニャウ 「7」「?」 
  自分  「5」「7」 = 「12」
という感じだった場合。ニャウは1/3の確率で現在のポイントは「17」になってると予想できます。
つまり1/3の確率でヒットせずに17で勝負にきます。
一方こちらの手札はどうでしょうか。
現在の合計は12ですので、ニャウが17と仮定するとまず勝てませんね。
なので選択は当然ヒットが最善。となります。

ということを踏まえるて
自分の手札とニャウのオープンカードによってどの手が最も最善に近いかというのをまとめるとこうなります。

f:id:tsubakurame-1913:20151116180209p:plain

H:ヒット
D:ダブルダウン
S:ステイ
です。

Aが手札に含まれる「ソフトハンド」と言われる手札の場合、下の表を。
それ以外の場合は上の「ハードハンド」の表を見てください。

なお、この表はSPカードの存在や「もう一枚引くニャウ」を考慮していません。(できません)
自分の手札にSPカードがある場合は、純粋に現在の合計値を表に当てはめても構いません。
ただ、SPカード追加により合計値が変動することが多々あるので一概に「これがいい」と言い切れないのが事実です。
SP絡めた手の場合はこの表は参考程度に、サイコロの目を読む事が必要になってきます。

さて、注意していただきたいのは
「この選択をしたから絶対に勝てるというわけではない」という点です。

SPの存在や、ニャウの気まぐれももちろんの事。
あくまでも「一番勝てる確率が高い選択はどれか」を表しています。

「この通りにやったけど負けたぞ!!」と言われても

f:id:tsubakurame-1913:20151116180946j:plain
ドゥドゥ「素晴らしく運が無いな君は」

ということ、ただそれだけです()


あくまでも確率的に勝ちやすい選択はありますが、運の要素が多分に追加されているブラックニャックでは
ほんとに運が悪いと負けがこむことも往々にしてあります。
だいたいそういう時はシューターに切り替えて取り返す()




自分の今の運勢をみながら、清く正しく熱く激しいカジノライフをお過ごしくださいませ。。。()






もし、今後希望があればシューターやスロットの解説も考えてるけど。。。
多分需要はないね()