技術

    Railsはちょっとお休みしてRubyを学んでいこう!

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    Rails難しくてあきたとかじゃな・・・ちょっとね。

    やはりRailsを学ぶにはRubyをおさえなくては!!(Rubyの本読んだだけで結局Rubyのプログラム書いてなかった)

    ということで、昔買った「Javaゲームプログラミングアルゴリズム」を引っ張り出してきた。

    中学生のころにこの本を買ってもらったものの、Javaのよく言われる「おまじない」に納得いかない、意味が分からなくて投げたことを思い出した。

    なのになぜC#をわりかしすんなり呑み込めたのだろうか・・・謎である。

    今思えばC#もおまじない系だと思うのだが・・・なぜか、そしていまJavaの本を読むとなぜ分からなかったのか分からない・・。

    何はともあれ、この本の最初に出てくるレースゲームもどきをRubyで書いてみた。

    一応できたものの、うーん。

    170408_Race1.png

    思っていたものと違う。

    そもそも、Rubyで書くべきではなかった。(今更)

    どうしても入力待ちが発生してしまう(スレッドはあきらめた)

    入力待ちになるため、考える時間がいくらでもあり、まー死なない。

    次はリアルタイム性の要求されない、ターン制のものを作ろう!

    初めてのRuby書いた学び!


    ・-- ++は使えない! += -=を使おう!
    ・偶数奇数の判定は % 2よりも even?を使うといいぽい!?(型の問題なのかな?%2だとうまくいかなかった)
    ・インスタンスメソッドとクラスメソッドの違いがよくわからないー


    一応ソースコードもあるよ。
    https://bitbucket.org/rinjugatla/race
    スポンサーサイト

    考えたことがあたってるとちょっとうれしくなるよね

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    ちょうど一年前くらいにとある理由でプログラムを書くことになった。

    ・読み込むデータは用意されている
    ・解析手法も用意されている
    ・結果の見せ方も大体既存の参考資料がある

    ・使用言語は自由
    ・とりあえず動けばいいらしい

    というゆるい開発だったので、これまでの趣味の延長で作り始めた。

    やはりGUIはあったほうが便利だろうということ、グラフや表で結果を示したかったこともあり、言語はC#を選んだ。
    (というより割とまともに扱えるものがC#以外にな・・・)

    読み込むデータは大体以下のようになっていた

    data.csv
    #ヘッダー開始(ない場合もあり、ない場合はヘッダー終わりもなし)
    #ヘッダー終わり
    日時,データ1-1,データ2-1,...(複数列)
    日時,データ1-2,データ2-2,...
    日時,データ1-3,データ2-3,...
    以下数千から数百万行

    ・解析手法でのちにデータを消すことが分かっている
    ・とりあえず動けばいいらしい

    ということから、今までの経験から得た技術のListを用いてデータを格納していることにした。

    Step0
    List1-1(データ1-1を格納)
    List1-2(データ1-2)
    List1-3(データ1-3)
    List1-4(データ1-4)
    List1-5(データ1-5)

    と順番に格納していくわけである。(データ2-は表記省略)

    解析する間に

    Step1
    List1-1(データ1-1)
    List1-2(データ1-2)
    List1-3(データ1-3)

    List1-4(データ1-5)

    Step1
    List1-1(データ1-1)

    List1-2(データ1-3)

    List1-3(データ1-5)

    と歯抜けになっていく。

    Forで回していたと思うので、ここはList.RemoveAt(Int32)でデータを抜いていった。

    しかし、遅い、遅い、待てども待てども終わらない。何が悪い?

    既存技術の組み合わせということがあって、同様の処理をするプログラムがフリーで公開されている。
    それらを用いると時間がかかるものでも1分程度、自作のものは1時間以上かかる。

    明らかにおかしい!
    いろいろな場所にブレークポイントを設置した結果、上記の箇所が原因であると特定された。
    (ここまでに一通り作ろうと思っていた機能をすべて盛り込み済みで、自分の開発した中では最大規模のプログラムになっており、特定作業も大変であった)

    どうやら、要素を削除するたびに、要素を前に詰めているようだった。
    言葉にすると伝わりづらいだろうか、昔作った画像が出てきたので参考に

    270407_prog.png

    数百万*数列あるデータに対して、列ごとにデータを一つ消すたびに後ろからデータを詰めていたらしく、計算量は莫大な量になっていたということである。

    今までは扱うデータ量が多くとも数千程度だったので、詰め詰めしても大して気にならなかったようだが、今回は数百万*十数と今までとは全く違う規模のデータ量なので問題が浮き彫りになった。

    消すとダメ・・・ダメ・・・なら飛ばせばいいじゃない!とひらめいた。
    お風呂に入っていた時だったような気がする。ぽけーっとしてるとひらめくことあるよね。

    270407_prog2.png

    要素を作成する際に、前後関係と要素データを合わせて持つようにする。

    Step0
    List1-1(データ1-1を格納、前なし、後1-2)
    List1-2(データ1-2、前1-1、後1-3)
    List1-3(データ1-3、前1-2、後1-4)
    List1-4(データ1-4、前1-3、後1-5)
    List1-5(データ1-5、前1-4、後なし)

    といった具合である。
    すると

    Step1
    List1-1(データ1-1を格納、前なし、後1-2)
    List1-2(データ1-1、前1-1、後1-3)
    List1-3(データ1-2、前1-1、後1-5)
    List1-4(データ1-4、前なし、後なし)
    List1-5(データ1-5、前1-3、後なし)

    といった具合になり、詰め詰めしなくともすむ。
    これは!すごいのでは!

    と、独自でクラスを作り始めたのだが、途中で「もしかして、自分が思いつくのなら既にあるのでは?」と考え、検索してみると

    「連結リスト(片方向リスト、双方向リスト)」

    というものが、これにあたるようで、C#にももちろん標準で実装されていると分かった。

    こちらを利用したところ、ほかのフリーソフト並みの爆速で処理が終わるようになった。
    今度のボトルネックはストレージからの読み込み速度だが、ここはいまだに難儀している。

    何はともあれ、

    「何が悪い?」

    から

    「消すとダメ・・・ダメ・・・なら飛ばせばいいじゃない!とひらめいた。」

    「もしかして、自分が思いつくのなら既にあるのでは?」


    やっとタイトルである、「考えたことがあたってるとちょっとうれしくなるよね」

    先人たちも同じところで躓いて、同じことを考えていたと分かるとうれしい!

    もちろん、そんなところで時間を(完全ではないにせよ)無駄にしないためにも基礎知識をきちんとつけておくべきではある。

    でも、うれしいよね。

    と、なぜ今更こういうことを書いたかというと、先ほども同じ経験をしたからである。

    ・ユーザ登録型
    ・ユーザごとに個人ページを持ち、ブログのようなことができる

    といったものをRailsの勉強で作ろうとしてるのだが、何分データベースもまともに触ったことがないので、データベースをどう作ればいいのかとここ数日悩んでいた。

    初期の想像

    Table ユーザ情報
    ID Name Password など
    1 Taro ababa
    2 Hana gegeg

    Table Taroさんのブログ
    記事ID Title Text など
    1 te fefef
    2 fdf fefe

    Table Hanaさんのブログ
    記事ID Title Text など
    1 te fefef
    2 fdf fefe

    これではユーザが増えるごとにテーブルが増える・・・テーブルが増えるとパフォーマンスが悪いと、ではどうすれば・・・。

    と先ほど思いついたのが

    Table ユーザ情報
    ID Name Password など
    1 Taro ababa
    2 Hana gegeg

    Table ブログ情報
    記事ID 投稿者ID Title Text など
    1 1 fefe cdcd
    2 2 df cece

    ブログ記事情報を一つのテーブルにまとめ、中に投稿者IDを作ればいいのだ!なるほど。

    これが正解らしい。

    と、いうことで、ふと前の経験を思い出したわけである。

    [覚書] C# 非同期処理の例外処理外エラー個所の特定

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    最近非同期処理を実装したプログラムを書いているのだが、非同期で例外処理をきちんとしていないとエラーがとても厄介なことになると学んだ。
    この場合のエラー個所の特定方法についての覚書。

    まず、非同期処理内で例外処理(try catch)をしていない場合に表示されるエラーについて。

    非同期処理内、例外処理なし
    タイトル「TargetInvocationExceptionはハンドルされませんでした。」
    追加情報「呼び出しのターゲットが例外をスローしました。」
    rainflow_async_error1.jpg


    非同期処理内、例外処理あり
    try{ /// }catch(Exception ex){ ex.ToString(); } とりあえず例外表示するだけ
    rainflow_async_error2.jpg


    同期処理内、例外処理なし
    タイトル「ArgumentOutOfRangeExceptionはハンドルされませんでした。」
    追加情報「インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。」
    rainflow_async_error3.jpg


    今回引っかかったのは、いろいろといじって、いじりすぎて、いざデバッグを実行したところ、”非同期処理内、例外処理なし”のエラーが表示されたが、いじった個所が多すぎてどこが問題なのかわからなかったことだ。
    とりあえず、該当しそうな箇所に try{ /// }catch(Exception ex){ ex.ToString(); } を入れてみたが”非同期処理内、例外処理なし”のエラーしか出ない。

    困ってグーグル先生に聞いたところ
    「トレースログを出力する」
    とよいらしいという情報を得た。
    が、ログを出力するコードを追加したもののログを出力する前にエラーが出ているようで追加の情報は得られなかった。
    (付け焼刃でコードを追加したので追加箇所が正確ではない可能性がある。)

    これは困ったとだめもとで
    ”非同期処理内、例外処理なし”のエラーダイアログの「例外の詳細をクリップボードに追加」を実行した。


    System.Reflection.TargetInvocationException はハンドルされませんでした。
    HResult=-2146232828
    Message=呼び出しのターゲットが例外をスローしました。
    Source=mscorlib
    StackTrace:
    場所 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
    場所 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
    場所 System.Delegate.DynamicInvokeImpl(Object[] args)
    場所 System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
    場所 System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
    場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    場所 System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
    場所 System.Windows.Forms.Control.InvokeMarshaledCallbacks()
    場所 System.Windows.Forms.Control.WndProc(Message& m)
    場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
    場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
    場所 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
    場所 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
    場所 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
    場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
    場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
    場所 System.Windows.Forms.Application.Run(Form mainForm)
    場所 Rainflow.Program.Main() 場所 C:\Users\xxxxxxx\Documents\Visual Studio 2015\Projects\Rainflow\Rainflow\Program.cs:行 19
    場所 System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
    場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
    場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
    場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
    場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    場所 System.Threading.ThreadHelper.ThreadStart()
    InnerException:
    HResult=-2146233086
    Message=インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
    パラメーター名:index
    ParamName=index
    Source=mscorlib
    StackTrace:
    場所 System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
    場所 System.Collections.Generic.List`1.get_Item(Int32 index)
    場所 Rainflow.DGV.d__3.MoveNext() 場所 C:\Users\xxxxxxx\Documents\Visual Studio 2015\Projects\Rainflow\Rainflow\Control.cs:行 316
    --- 直前に例外がスローされた場所からのスタック トレースの終わり ---
    場所 System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.b__6_0(Object state)
    InnerException:


    Message=インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
    この情報は大きい!そしてその下の
    場所 Rainflow.DGV.d__3.MoveNext() 場所 C:\Users\xxxxxxx\Documents\Visual Studio 2015\Projects\Rainflow\Rainflow\Control.cs:行 316
    ここが問題だそうだ。

    ということで、修正しました。
    例外処理はちゃんとしようね。
    メッセージ表示するだけでも効果あるある。

    タイマー式シャットダウン用batファイル v4

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    ダウンロードし、ダブルクリックするとできます。

    終了予定までの時間を分で入力すると、内部で秒に変換してshutdownコマンドを実行します。

    実行してしまった後に、処理を中断したい場合は何かしらのキーを押しキャンセルすることができます。

    batファイルを閉じる場合は右上から閉じるボタンを押してください。キャンセルして閉じる、キャンセルせず閉じるボタンで閉じる、どちらもシャットダウンは中止できます。

    更新履歴

    v1
    12/11/22
    ファイル名をpoweroff.batからpoweroff2.batに変更
    時間に文字列が混入した場合即座にシャットダウンが実行されるため、入力文字列を数値か文字列か判定する処理を追加しました。
    文字列と判定された場合は再度時間入力を求めます。

    v2
    12/11/23
    なぜか正常に動作していないので前のファイルに差し替え。
    数値以外を入力すると即座にシャットダウン処理を始めるので注意

    v3
    13/10/01
    不具合修正完了

    v4
    16/07/04
    通常のシャットダウン処理と強制シャットダウンを選べるように変更。


    ダウンロード

    ソース v4
    @echo off
    :LOOP
    rem /////////
    rem 初期化
    set pushKey=
    set SEC=
    set X=
    set TIME=
    rem /////////
    set MIN=60
    set TIME=0
    set /p SEC="自動シャットダウン予定までの時間(分)を入力してください >"
    set /a X=SEC
    if defined X set X=%X:0=%
    if defined X set X=%X:1=%
    if defined X set X=%X:2=%
    if defined X set X=%X:3=%
    if defined X set X=%X:4=%
    if defined X set X=%X:5=%
    if defined X set X=%X:6=%
    if defined X set X=%X:7=%
    if defined X set X=%X:8=%
    if defined X set X=%X:9=%
    if not defined X set /a TIME+=SEC*MIN
    if %TIME%==0 echo 数列のみ有効です。
    if %TIME%==0 goto LOOP
    rem /////////
    echo %SEC%分後にシャットダウン処理を実行します。
    echo;
    echo 通常のシャットダウン処理を実行する場合は1
    echo アプリケーションを強制的に終了しシャットダウンする場合は2
    set /p pushKey= "処理モード:%pushKey%"
    rem /////////
    if "%pushKey%"=="1" (
    echo 処理を実行します。
    shutdown -s -t %TIME%
    echo 処理を中断する場合は何か押してください。
    pause >nul 2>&1
    rem set /p pause = %pause%
    rem if "%pause%" == ("")
    shutdown -a
    goto LOOP
    rem /////////
    ) else if "%pushKey%"=="2" (
    echo 処理を実行します。
    shutdown -s -f -t %TIME%
    echo 処理を中断する場合は何か押してください。
    pause >nul 2>&1
    shutdown -a
    goto LOOP
    rem ) else if "%pushKey%"=="n" (
    rem echo 処理を中断しました。
    rem goto LOOP
    rem /////////
    )else (
    echo 1/2以外は認識しません。処理を中断しました。
    goto LOOP
    )


    作った経緯
    眠たい、でも累計ログイン時間によるアイテム報酬を受け取りたいというそこのあなたに小技を一つ。

    つけっぱなしは電気の無駄、でも帰りが遅くて12時を回ってしまうかもしれない。

    きちんとログインアイテムを受け取りたい、どうしたらいいの?

    答えは簡単、深夜0時を回って4時間でPCの電源が落ちるように設定して寝ればいいだけです。

    新しくツールを入れる必要はなく、windowsに標準で入っているコマンドプロンプトでコマンドを入力すると簡単にできます。

    コマンドプロンプトを起動する方法は、

    window7,vistaの場合
    [windowsメニュー]→[全てのプログラム]→[アクセサリ]→[コマンドプロンプト]

    検索から実行する場合は
    windows7,vistaの場合
    [windowsメニュー]→[プログラムとファイル検索]→[cmd]と入力してcmd.exeを起動

    シャットダウンするコマンドは

    shutdown

    これに、オプションで、時間(秒)を指定します。

    -t sec

    つまり、

    shutdown -t 60

    を入力し、エンターで実行すると、実行から60秒後にPCがシャットダウン処理を始めます。

    1時間+10分
    shutdown -t 4200

    2時間+10分
    shutdown -t 7800

    3時間+10分
    shutdown -t 11400

    4時間+10分
    shutdown -t 15000

    アイテムは若干時間に誤差があるようなので、上の命令文の時間はX時間+10分の猶予時間が足してあります。

    batファイルを作ったので上の使い方がわからない人用に載せておきます。




    動作環境はXP以降

    タイマー式メモリー解放用batファイル v2

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    empty.exeを利用した定期メモリー解放用batファイル

    ①empty.exeを入手する


    Windows Server 2003 Resource Kit Tools
    上記ツールをダウンロードし、WinRARなどを使い、中にあるempty.exeを取り出す。
    面倒な場合はインストールしてしまい、empty.exeを入手し、アンインストールするという方法でも可。

    ②empty.exeをC:\Windows\System32\に移動する


    要管理者権限

    ③ダウンロードor batファイルを作成


    ダウンロード

    ソース

    @echo off
    :LOOP
    rem MODE 1:起動時に時間入力 2:固定時間毎に処理
    set MODE=2
    rem MODE 2の場合の時間
    set MODE_2_MIN=60
    rem /////////
    rem 初期化
    set pushKey=
    set SEC=
    set X=
    set TIME=
    rem /////////
    set SEC=60
    set TIME=0
    if %MODE%==2 goto MODE2
    set /p MIN="自動メモリ開放までの時間(分)を入力してください >"
    set /a X=MIN
    if defined X set X=%X:0=%
    if defined X set X=%X:1=%
    if defined X set X=%X:2=%
    if defined X set X=%X:3=%
    if defined X set X=%X:4=%
    if defined X set X=%X:5=%
    if defined X set X=%X:6=%
    if defined X set X=%X:7=%
    if defined X set X=%X:8=%
    if defined X set X=%X:9=%
    if not defined X set /a TIME+=SEC*MIN
    if %TIME%==0 echo 数列のみ有効です。
    if %TIME%==0 goto LOOP
    goto MEMCLE
    :MODE2
    set /a TIME+=SEC*MODE_2_MIN
    set MIN=%MODE_2_MIN%
    rem /////////
    :MEMCLE
    empty.exe *
    echo %MIN%分後に自動メモリ開放します。
    echo キー入力で強制的にメモリ開放します。
    TIMEOUT /T %TIME%
    goto MEMCLE


    ④設定、実行する


    batファイル実行時にメモリ解放時間を入力する場合は設定不要。
    毎度設定するのが面倒な人は
    set MODE=1
    set MODE=2
    に変更する。

    また、時間の指定は
    set MODE_2_MIN=60
    の数値を変更する。(単位は分)


    v1
    初公開

    v2
    16/07/04
    MODE=2の時に再解放までの時間が表示されていない不具合を修正
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。