投稿記事

無料プランの記事 (168)

Heliodor 2021/02/07 16:33

ログをいい感じで残す その2

前回の話 の続きです。

デバッガー、コンソール、テキストファイルの3か所にそれぞれログを流すようにした、という話でした。


というわけで、しばらくこのやり方でやっていたのですが、ちょっとマズイことに気がつきました。ヴィータ大脱出を二つ同時に起動するとテキストファイルに出したログが破綻するんです。

同じログファイルを二つのEXEが使おうとするため後から起動した方のEXEがログをクリアしてしまったり、先に起動した方のEXEがファイルをロックしていて後から起動したEXEは一切ログの書き込みができなかったり……考えてみれば、そりゃそうですよね。


とても分かりやすい図解。サムネがやる気のないアレになってしまう対策

確かプロセス間で同期を取る仕組みがあったと思うので、それを使って互いに配慮しながらログを書き込もうか……と思ったこともありましたが、面倒くさかったのでもっと単純に、ヴィータ大脱出を複数起動出来ないようにしました。
もっとも、ヴィータ大脱出を複数起動してしまうとセーブファイルの書き込みで問題が発生したりするので、最終的には禁止するつもりではありましたが。


ちなみに今開発しているあやかし紅白戦では、複数起動しても問題なくログファイルが書き込めるようにしてあります。
複数のEXEが一つのログファイルを使えるようにしたため、誰が書き込んだのか区別できるようにプロセスIDを追記するようにしました。
また、ログの見た目を整えるためにインデントする機能なんかがあったのですが、それは廃止しました。インデントありの行とインデント無しの行が混在する可能性があるからです。


ところで、多重起動の他にもう一つ問題がありました。
起動時にログをいったんクリアしていたため、最後に起動したときのログしか残らないという点です。
つまり、強○終了が発生した後、一度でもゲームを起動したら強○終了時のログファイルが上書きされるって事なんですよね。
テスト配布した時のエラー報告なんかでこの事故が多発しました。
(「不具合が起きた!」というユーザーさんから何の問題も起きていないログファイルが送られてくるという……)


それと以前、強○終了時にその時のエラー内容をログに記録云々という話をしましたが、それはあくまでも強○終了したときに特別に作られるエラー報告用のファイルであって、今回の話のように普段から記録しているようなログファイルとは異なります。

そこで、ログファイル名に日時を入れて、毎回新しいログファイルに書き込むようにしたのですが……まあ、すぐにやめました。ファイルがどんどん増えてしまうんです。ストレージ(Cドライブ)が64GBしか無いド〇キノートでは(ry

じゃあ古いファイルを自動で消せばいいじゃないか?となったんですが、「ファイルを自動で消す」という動作が嫌な予感しかしなかったのでやめておきました。

ファイル名に日付が入るということは、あらかじめファイル名がビシッと決まってはないという事です。なので、消してもいいファイルなのかどうかをファイル名から自動判別させないといけません。
が、それだと関係無いファイルを消してしまうという事故がありそうで怖かったからです。

生成したログ名を別のファイルに記録しておいて、それを参照するという手もあります。
ただ、ユーザーが誤ってそのファイルを消してしまった場合にはログ名が分からなくなり、ログを削除できなくなってしまいます。まあ手動で消せばいいんですが……。

ログ専用のフォルダを作って、その中身は問答無用でログとみなすという方法もありますが、たかがエロ同人ゲームでログ専用のフォルダとか余計なもの作られるってのもねえ……ということで却下。

悩んだ挙句、ログファイルは一つだけにして、ログを開くときにファイルサイズを調べ、10MB以上のサイズだったら10MBになるように古い行を削る、という仕様にしてみました(ちなみに10MBという数字は割と適当に決めたものです)。

仕組みは単純で、いったんログを読み出しモードで開いてバッファに全テキストをロードして閉じ、テキストが目的のサイズになるように加工し、今度は書き込みモードで開いて書き込むというものです。
なんかもっとスマートに出来そうな気もするのですが、起動時の一回しかやらない処理なのであまり追求しないことにします。
やってることはリングバッファと同じですね。

これもしばらくはOKだったのですが、かなりの長時間起動していた場合に一回の起動でログが10MBを超えてしまう時があるんですね。
そうすると、起動して最初の方で記録した内容がいきなり消されてしまうんですよ。



結局、紆余曲折を経て最終的には過去3回分より古い部分を削除する、という仕様で落ち着きました。

まず、ログの書き込み開始するときに必ず区切り用のテキストを出力するようにします。
起動時にログテキストをロードし、区切りテキストを後ろから検索して、3つ目の区切りテキストが見つかったところで、それよりも先頭側のテキストを削除して再保存する、いうものです。

ちなみに区切りテキストは

"------------------------\n"

みたいに、いかにも区切ってますって感じのテキストにしておきます。
こうすればテキストエディタでログを見たときにも、どこが区切りなのかすぐに分かります。
これでファイルサイズとは無関係に、必ず過去3回分までのログを保存できるようになりました。



ところでログを見るときですが、テキストエディタでももちろん大丈夫ですが、テキストログ用のビュワーというものが探すといくつも見つかります("log text viewer とかで検索すると見つかります)。
こういうのを使うと、特定の単語を含む行(例えば "ERROR" とか)を色付きで表示してくれるだとか、ログのテキストファイルをずっと監視していて、ログファイルに何か書き込まれたらすぐに反映してくれるとか、そういう便利な機能があります。

もっとも、コンソールウィンドウに色付きでログテキストを流すようになってからはそういうツールを使わなくなり、あとでログファイルを見るときも普通のテキストエディタで済ませるようになりましたけど。


こうして改めて見ると行き当たりばったりと試行錯誤の連続ですね!

HAHAHA!
(笑い事ではない)










今回もおまけのどうでもいい話。



CPU+マザボ買い替えは見送るとして、再利用可能なグラボだけでも先に買っちゃおうかなぁとPCパーツ見てみましたが、なんでグラボこんな値上がりしてるの!?


再びヤル気を失い飲んだくれるnVidia信者

今は1050ti使っているんで、せめて1650か1660あたりに……と思っていたんですが、こんなに高かったっけ?と目を疑いました。
つーか価格.comで売れ筋1位がRTX3080って嘘やろ!?(15万円ぐらいする)
どうせ高いの買うならハイエンド買っちゃえ!ということなんでしょうかね?
そーいやRyzenも5950Xとかもあの値段で売れまくって品薄なんですよね。
私の知らんところで景気良くなってるのか……?




ゲームの動作検証用に最近のRADEONも一つ欲しいなあなんて思っていたんですが、こっちも値上がりしていておいそれと手を出せそうもありません。

半導体値上がりかー。
個人レベルの同人ゲーム制作者には辛い時代ですねぇ。


冬場はマイニングPCが暖房器具代わりになるらしいんで、狙い目は夏頃……?

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

Heliodor 2021/02/03 22:26

ログをいい感じで残す その1

プログラム話です。
今回はログについて。

ログを出力する方法はいくつかあると思いますが、ヴィータ大脱出を始めヘリオドール製ゲームは基本的に3つの方法で出力しています。
3つのうちのどれかに出力という意味ではなく、1つのログを3つの方法で同時に出力という意味です。



1つ目の方法はデバッガーに対するメッセージ出力で、これは開発中に最も役に立つものです。
デバッガーへのメッセージというのは、VisualStudioから実行した時に「出力」ウィンドウに表示される文字列です。
それをやるには OutputDebugString 関数を使います。

ちなみに、この時出力する文字列の先頭に"ファイル名(行番号)" という形式でファイル名と行番号を入れておけば、VisualStudioの出力ウィンドウ内でログメッセージをダブルクリックして該当箇所に飛ぶことができます。

void OutputDebugStringEx(const char *file, int line, const char *msg) {
    char s[1024];
    sprintf(s, "%s(%d): %s\n", file, line, msg);
    OutputDebugString(s);
}

みたいな関数を作って _FILE_ と _LINE_ を引数に呼び出せばオッケーです。
この、ダブルクリックで該当箇所にジャンプと言うのは単純ながらすごく便利な機能なんですよね。

ちなみに OutputDebugString で出力したテキストというのは赤の他人でも見ることができるので、気になる場合は配布時に関数の呼び出しを切っておく必要があります。



2つ目の方法はコンソールウィンドウに対する出力です。
これはデバッガーがない環境でリアルタイムにログを見たい時なんかに便利です。

WinMain なプログラムでコンソールウィンドウを表示させるには AllocConsole 関数を使います。
ただ、Windows プログラムでは stdout がコンソールウィンドウに接続されておらず
printf で出力した文字列は闇の彼方に消えてしまうので

freopen("CON", "w", stdout);

を実行して自分で stdout と コンソールウィンドウと接続する必要があります。
これで普通に printf が使えるようになります。

ちなみにコンソールを閉じるには FreeConsole を使いますが、それよりも前に

freopen("NUL", "w", stdout);

を実行しておいて、コンソールをstdoutから切り離さないといけません。
そうしないと FreeConsole を呼んでもコンソールが閉じません。
まあ、アプリが終了すれはコンソールも勝手に閉じますけど。

コンソールウィンドウは文字の表示に特化してるだけあって、お手軽なんですよね。
ログをリアルタイムで表示するための専用ウィンドウとか自前で作らなくて良いし、工夫すればエラーメッセージは赤色で、警告メッセージは黄色で表示とかできますし。

コンソールに流れているログの色をかえるだけで、見やすさが完全に変わります。
これについては SetConsoleTextAttribute で検索してみてください。

製品版では AllocConsole を呼ばないようにすればコンソールウィンドウは表示されませんが、たとえ WinMain なプログラムであったとしてもコマンドプロンプトから EXE を起動した場合は普通に printf できてしまうため、見られたくないログがある場合は配布時に printf の呼び出しを切っておく必要があります。


3つ目の方法は、テキストファイルに出力するものです。
ここでは何も普通のテキスト形式で出す必要はなく、HTMLの書式で出せば文字の色を変えたり太字にしたり該当箇所にリンクを張ることもできます。

実際、最初の頃はヴィータ大脱出のログをHTML形式で出すようにしてました。
しかし途中でやめてしまいました。HTMLだとタグも出力しないといけないので全体の文字数が増え、ファイルの書き込みの負担がかかる&ログファイルがでかくなってしまうんです。

これ、製品版ではログ自体が少ないので問題ないのですが、開発中のバージョンだと細かいデバッグメッセージも全部記録しているのでわりとバカにならないんです。
ストレージ(Cドライブ)が64GBしか無いド〇キノートでは自殺行為。

それと、HTMLというのはタグを開くだけでなく閉じる必要もありますが、当たり前ですけど強○終了したときには終了タグが出せないんですよね。
いや、別に不完全なHTMLであってもブラウザで普通に表示できますが、なんか気になりまして。

そんなわけで、普通に fopen の書き込みモードで開いて、普通にテキスト書き込んで、ってやっていました。
この場合、fprintf の直後に fflush を置いて、バッファ内の文字列をすぐに吐き出させるように気をつけます。
そうしないと強○終了が発生したとき、内部バッファに文字列を溜め込んだまま逝ってしまうためにどの時点で強○終了したのかが分からなくなります。

ちなみにログファイルは毎回新規書き込みモードで開いています。
起動のたびに古いログが消え、まっさらなログファイルから始まるイメージです。



そんなわけで、しばらくは上記のやり方でやっていましたが、ちょっとマズイことに気がつきました。


次回に続きます。











おまけのすごくどうでもいい話。


なんで新しいCore i9、コア数減ってるの……。


今更Rokcet Lake-Sのことを調べてすっかりヤル気を失つてフテ寝するIntel信者

さすがにPC重くて不安になってきたけどRyzen 5000番台は全然売ってないし、Ryzenの攻勢で値下がりするであろうCore i系行っちゃうか!?とか目論んでいたんですが、もうしばらく様子見かな……。
やはりSandy Bridge最高。

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

Heliodor 2021/01/26 20:09

麻雀プログラム

楽しみにしていたドラクエ11が3D酔いであんまりプレイ出来なかったので正月休みを有意義に使った話です。つまり、また雑談のようなものです。



実は昔から一度作ってみたかったものに麻雀ゲームというのがあるのですよ。
正確に言えば、ゲームそのものを作りたいのではなく、ただ単に役判定やテンパイ判定部分を自分で作ってみたかったというものです。

これは学生時代に一度作ろうとしたのですが、まあ上手くいきませんでしたね。それっきり麻雀プログラムに手を出すことはなかったのですが……ちょいと時間が出来たので、思い立って再び挑戦してみることにしました。
(あ、もちろん例のアクションゲームは毎日コツコツ作り続けてますよ?)



ここから先は、麻雀のルールを知っているという前提で進めていきます。

麻雀の役の形は基本的に4面子1雀頭で、例外として国士無双と七対子がありますよね。
なので最初に例外形である国士無双と七対子が成立しているか調べます。

まず国士無双ですが、これは他のどの役とも複合しない(天和などの特殊パターンを除く)ので、真っ先に調べて、国士が成立していればその時点で役を確定させます。

次に七対子の形になっている場合ですが、一見七対子のようでいて実は字一色二盃口が成立している可能性もありますし、さらに清一色混一色タンヤオなどと複合しますよね。

例えば……


七対子の形になっていますが、これを七対子2ハンにされたら暴動が起きますよね。

というわけで、暴動を防ぐためにも「七対子の形になっているかどうか」の結果に関係なく、続けて4面子1雀頭が成立しているかも調べます。

この4面子1雀頭についてのチェックが一番面倒なのですが、今回は再帰検索を使って可能な組み合わせを全て列挙する方法を取りました。

まず手牌が理牌(ソート)されているという前提で、次のように処理を進めていきます。



※関数の外側に面子スタックを用意する(このスタックには刻子、順子、対子が入る)
※関数は引数として「ソート済みの牌配列」を取る。初期呼び出しでは14個指定する

1.牌配列が空っぽなら…
  →面子スタックの内容を見る。
  スタックに4面子1雀頭存在していれば、最初の関数呼び出しで指定した手牌(14牌)はアガリの形になっている
  この結果を記録して Return する

2.最も左の牌を使った刻子が存在するなら…
  →その刻子を取り除いてスタックにPushする。
  ・短くなった手牌を引数にして、再帰呼び出しを行う
  ・スタックをPopする

3.最も左の牌を使った順子が存在するなら…
  →その刻子を取り除いてスタックにPushする。
  ・短くなった手牌を引数にして、再帰呼び出しを行う
  ・スタックをPopする

4.まだスタックに対子が入っていないなら…(つまり、まだ雀頭を見つけていないなら)
    最も左の牌を使った対子が存在するなら…
    →その対子を取り除いてスタックにPushする
    ・短くなった手牌を引数にして、再帰呼び出しを行う
    ・スタックをPopする
    
5.Return する


この関数を呼び出して戻ってきたときには、この14牌を4面子1雀頭の形に分割するときの全ての組み合わせが列挙されている事になります。


ところでこれは14牌を使って「4面子1雀頭の形になる組み合わせ列挙する」やり方なので、13牌だけを使ってテンパイチェックするということができません(余り牌を許容しないアルゴリズムなので)

その場合は少し処理を追加して次のようにします



※関数の外側に面子スタックを用意する(このスタックには刻子、順子、対子が入る)
※関数の外側に余り牌スタックを用意する
※関数は引数として「ソート済みの牌配列」を取る。初期呼び出しでは13個指定する

1.牌配列が空っぽ?
 →面子スタックの内容を見る。
  ・スタックに4面子0雀頭が入ってる?
   →余り牌スタックに牌が1個入っているはず。その牌で単騎待ちテンパイである。
   ・この結果を記録して Return する
      
   ・スタックに3面子1雀頭が入ってる?
    →余り牌スタックに牌が2個入っているはず。
    その2牌がどんな組み合わせになっているかを調べる
       
   ・余り牌スタックの2牌が同じ?(対子)
    →その2牌と、スタックにある雀頭牌でのシャンポン待ちテンパイである。
     ・この結果を記録して Return する
        
     ・余り牌スタックの2牌が数字牌かつ隣同士か?(辺張 or 両面塔子)
      →その2牌の隣の牌による辺張 or 両面待ちテンパイである。
       ・この結果を記録して Return する
        
     ・余り牌スタックの2牌が数字牌かつ1個飛ばしか?(嵌張塔子)
      →その2牌に挟まれた牌による嵌張待ちテンパイである
       ・この結果を記録して Return する
        
     ・ここまで処理が来たら、余った2牌は塔子になっていない。つまりテンパイしていない
      余った2牌のどちらかに別の牌がくっつけばテンパイになるので、イーシャンテン状態である
      この結果を記録して Return する
        
2.最も左の牌を使った刻子が存在する?
    →その刻子を取り除いてスタックにPushする。
    ・短くなった手牌を引数にして、再帰呼び出しを行う(手順1に戻る)
    ・スタックをPopする

3.最も左の牌を使った順子が存在する?
    →その刻子を取り除いてスタックにPushする。
    ・短くなった手牌を引数にして、再帰呼び出しを行う(手順1に戻る)
    ・スタックをPopする
    
    ※補足
     例えば「111223499…」と並んでいた場合、
     一番左の1を使った順子123をチェックする。
     1,2,3がそれぞれ1個以上が存在するので、
     それらを取り除くと残った配列は「112499…」になる

4.まだスタックに対子が入っていない?(まだ雀頭を見つけていない?)
    最も左の牌を使った対子が存在するなら…
    →その対子を取り除いてスタックにPushする
    ・短くなった手牌を引数にして、再帰呼び出しを行う(手順1に戻る)
    ・スタックをPopする
    
5.一番左にある牌を余り牌として取り除き、余り牌スタックにPushする
  ・短くなった手牌を引数にして、再帰呼び出しを行う(手順1に戻る)
  ・余り牌スタックをPopする

6.Return する

以上の処理がすべて終わって呼び出し元に戻ってきたとき、テンパイしているならその待ち牌がすべて記録されています。
待ち牌が全く記録されていなければテンパイならず、ということです。



細かい話ここまで

うーん、なんだかわかりにくいような……。
再帰を含むプログラムって、すごく説明しづらいですね。

フォロワー特典で判定テスト用の実行ファイルと、判定部分のソースコードを付けておきますので、興味がある方はどうぞ。



ついでにテンパイ&シャンテン数、待ち、鳴きの有無、役の判定、点数計算もつけておきました。あんまり動作テストしてないですけど。

(※天和とかカンとか嶺上開花とかの役は未実装です)

フォロワー以上限定無料

まずは無料プランで様子見を。 お気軽にフォローしてみて下さい。

無料

【 500円 】プラン以上限定 支援額:500円

このバックナンバーを購入すると、このプランの2021/01に投稿された限定特典を閲覧できます。 バックナンバーとは?

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

Heliodor 2021/01/22 22:13

あまり参考にならないアニメーションの描き方

今回の記事はフォロワー以上限定です。

フォロワー以上限定無料

まずは無料プランで様子見を。 お気軽にフォローしてみて下さい。

無料

【 500円 】プラン以上限定 支援額:500円

このバックナンバーを購入すると、このプランの2021/01に投稿された限定特典を閲覧できます。 バックナンバーとは?

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

Heliodor 2021/01/17 18:16

雑談

正月は結構のんびりと過ごしました、ヘリオドールです。
今回の記事はまたゲーム製作とは全然関係無い雑談(正月休み中の話)をふたつほど。


正月早々ガスコンロの電池が切れる

ガスコンロはガスだけじゃなくて電池が必要なんですよね。つーか点火できない。


単一電池なんて何年ぶりに見ただろう……もちろん予備なんてありゃしません。
近所のスーパーは正月休みだしコンビニのはクソ高お高いし、結局作り掛けの料理を放置したままド○キホーテまで買いに行ってきました。

今年も早速お世話になりました。ありがとうド○キホーテ。






健康と不健康

毎年この時期は風邪気味なことが多く、図らずも寝正月なことが多いのですが今年は風邪引きませんでした。

マスク手洗いうがい徹底しているお陰かなー?めんどくさいけど効果はあるんだなー、健康サイコー!などと油断して酒飲みながら楽しみにしていたドラクエ11Sをプレイ。

すんごい勢いで酔う。


ヴぉえぇぇぇぇぇ

3D酔い+酒酔いで床をゴロゴロ転げ回りました。世界が回るというのを人生初体験
なるほど、世界が回っているように感じるからバランスを取ろうとして自分で転がり続けるんですね。

結局寝正月になりました。
ドラクエはカメラに慣れるまで少しずつやります……。

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

3 4 5 6 7 8 9

限定特典から探す

記事のタグから探す

月別アーカイブ

記事を検索