C++ テンプレート引数で与えられる正の整数2つの比較

C++ で、正の整数2つをテンプレート引数で渡して is_greater_eq<lhs, rhs>::value みたいなことがしたくて、 数週間悩んだ挙げ句、下記のようなインチキなコードを思いついたのですが、 もっとマシな方法ってあったりするんだろうか…。

#include <iostream>

template <int lhs, int rhs>
struct is_greater_eq 
: public is_greater_eq<lhs - 1, rhs - 1> {
};

template <int rhs>
struct is_greater_eq<0, rhs> {
    constexpr static bool value = false;
};

template <int lhs>
struct is_greater_eq<lhs, 0> {
    constexpr static bool value = true;
};

template <>
struct is_greater_eq<0, 0> {
    constexpr static bool value = true;
};

int main()
{
    std::cout << is_greater_eq<5, 4>::value << std::endl;
    std::cout << is_greater_eq<5, 5>::value << std::endl;
    std::cout << is_greater_eq<4, 5>::value << std::endl;
}

Cities Skylines 路面・路上交通の地味な比較

バニラでの話。

交通機関 1編成の
最大乗客
2車線 4車線 6車線 専用軌道 地下 車庫 一般車との
並走
バス 30 × 不要 あり
路面電車 90 × 不要 あり
モノレール 180 × 不要 なし
  • 車庫
    • バスの車庫は路線までいくら遠くても道路で繋がってさえいればいつかは配車される
    • 路面電車は、車庫から設定路線まで路面電車の軌条で繋がっていないといけない
  • 停留所・駅
    • バスと路面電車は駅のないところでも停車地点設定が可能
    • モノレールは駅を設置しないと停車できない
      • 既存道路上に駅を設置したい場合は、既存道路を一度撤去する必要がある (軌道はアップグレードで設置可)
  • 地下
    • バスは普通に地下道を通れる
    • 路面電車軌道も地下にできる
    • モノレール軌道は地下にできない

というわけなので個人的には…

  • バス路線を設定してみたものの収容しきれない乗客数の場合は、路面電車にアップグレードするのが簡単
    • 地下鉄や鉄道の培養線みたいなものは初めバス、収容しきれないときは路面電車
  • あらかじめ需要大と分かっている場合はモノレールか地下鉄
    • モノレールは景観的には良いが、駅の設置がとても面倒で、一般道路や鉄道と立体交差させる場合はモノレール側を地下にすることはできない
    • 地下鉄は地上の景観にほぼ影響しないので個人的にはつまらない感

ゲーセンミカドのときメモ系ゲーム実況で出てくるキャラ通称一覧

通称 キャラ名 由来
パンク 藤崎詩織 恋愛ゲームヒロインなのに髪の毛の色(赤)がパンクなことから
肉便器 虹野沙希 運動部マネージャー → 肉便器
スタンド使い 清川望 ぱずるだまにおいてゲーセンを水没させていることから
矢尾一樹 紐緒結奈 ジャンク屋好き → ジャンク屋
ジュドー・アーシタ矢尾一樹(ジュドー役の声優)
もろこみ 朝日奈夕子 デートに遅刻した際のセリフ「電車がもろ混みでさ〜」から
ヤクザの娘 古式ゆかり 本編ゲーム内での設定
キャバ嬢 鏡魅羅 見た目から
アテナ、城戸沙織 鏡魅羅 夏服が聖闘士星矢のアテナ(城戸沙織)の服に似ていることから
so fan 片桐彩子 デートが楽しかったときのセリフから
エセ外国人 片桐彩子 上記 "so fan" に代表されるように、
セリフに織り交ぜて来るルー大柴ばりの英語から
ガーギー 片桐彩子 ゲーム中で本人が好きな芸術家
いもうと 早乙女優美 主人公親友の妹
優美ボンバー 早乙女優美 ときめきメモリアル対戦ぱずるだまで使用する技名から
ヘルメット 美樹原愛 髪型から

std::numeric_limits<T>::is_exact ??

std::numeric_limits<T>::is_exact の言わんとしていることがサッパリ分からない…。 cppreference.com とかじゃ分からず、 ISO も読んでみたが情報量としては大して変わらない。

static constexpr bool is_exact;

19 True if the type uses an exact representation. All integer types are exact, but not all exact types are integer. For example, rational and fixed-exponent representations are exact but not integer.

20 Meaningful for all specializations.

"exact representation"、「正確な表現」というのが何を言わんとしているのか…。 とりあえず、この文章から分かることは

  • 組み込み型の整数型はすべて exact
  • exact な型すべてが整数とは限らない
    • 例えば有理数表現や指数部固定の表現*1は exact だが整数ではない

ということなんだが、結局 "exact representation" の定義はよく分からない。

ある型に対して「表現できる」と期待される値域において変換上の誤差が生じるか、みたいな話だろうか…。

表現する型 表現できると期待される値域 is_exact
整数型 整数型それぞれの値域 true
有理数 分母分子それぞれの値域 true
固定小数点型 内部表現の整数型の値域と固定指数によって決まる値域 true ?
浮動小数点型 false ?

「?」 のついてるところが私の理解の及ばない部分。 浮動小数点数は指数部と仮数部の兼ね合いで「実世界上での同じ値を表現できる複数の値」が存在すると言ってるんだろうか…。 でも、それも仮数部が必ずケチ表現だとなれば単一の表現にならないっけ…?? んーむ、わからん…。 知りたかったのは固定小数点型は exact か否か、だったのでとりあえず忘れてもいいんだけど、スッキリしないな…。

*1:いわゆる固定小数点のことだろう

signed char に対する単項演算子

以前 int16_t に対する +=  とかでもハマったんだが、 符号を反転させる単項演算子 - とかでも int への格上げが起きるようで

template <typename T>
T my_abs(T n) {
  return ((n >= 0) ? n : -n);
}

みたいなコードは Tsigned char とかのときはコンパイルが通らない (コンパイルオプションが -Werror=conversion かそれ相当になっている時)。 なお、 error 自体は三項演算子? のところで出ているような表示になるので注意 (clang の場合)。

template <typename T>
T my_abs(T n) {
  return ((n >= 0) ? n : static_cast<T>(-n));
}

こうすれば通るが微妙にカッチョ悪いよなぁ…。

ちなみに std::abs() を使わず車輪の再発明したのは 実際には下記のように constexpr function にしたかったため。

template <typename T>
constexpr T my_abs(T n) {
  return ((n >= 0) ? n : static_cast<T>(-n));
}

2019年末特別企画: シューティングゲーム ポラックスを工学?する - Engineering analysis for POLLUX -

ゲーセンミカド さんのシューティング動画でおなじみ? 韓流シューティングゲーム「ポラックス POLLUX」。 動画内でも触れられているように数々の不可解な挙動がある。 それらについてソフトウェア・エンジニアリング的に 「どういった設計・実装をすればそういう挙動になるのか?」 勝手に妄想してみようという無責任極まりない企画。

なので、「こんなの工学じゃない」みたいなマジレスは全力却下なのでw

前提

ポラックスとは?

  • NTC という韓国メーカーが作ったアーケード向け縦スクロールシューティングゲーム
    • Copyright 表記は1991となっているが、日本に入ってきた時期はもう少しあとの模様
    • 普通に説明がつかない挙動がいくつかある (後述)

本記事の筆者について

  • 職業ソフトウェアエンジニア歴22年、ゲーム開発経験はほぼなし
    • 8bit PC の BASIC でプログラム書いてから、だと35年くらい
    • ゲーム開発は仕事としては経験なし (学生時代に 8bit, 16bit PC で趣味としてはやっていたが)
    • デバイスドライバーとかよりは、 MW 〜 アプリ層の経験が圧倒的に多い
    • プラットフォーム的には 16bit 〜 64bit, RTOS〜Window〜LinuxUNIXクラウド、組み込み〜ゲーム機(開発してたのは非ゲーム)〜PC〜HPC〜サーバーソリューションと守備範囲は広め
    • 言語的には C++ が圧倒的に経験多い、あとは C、shell、JAVAOctavePython、Shader Language、OpenCL、…

参考資料

ポラックスの不可解な挙動まとめ

  • 自機のショットをパワーアップさせたり 3way に変更すると、サブウエポンのミサイルの発射数が目に見えて少なくなる (発射間隔が長くなる)
  • 敵や敵弾の多い場面で1P側のボム発射ボタンおよび2P側のレバー右が効かなくなる
    • 「ボムは出るまで押せ」「ボムに連射つけて欲しい」
  • ハードウェア連射装置を使うと、敵が多くいる場面で自機の弾が出なくなる
    • ゲーム内でアイテムと取ると適用されるソフトウェア連射は問題ない
  • ボス敵が突然固くなり数分間攻撃を当て続けても倒せなくなる (普段はもちろんもっと短い)
  • ブラックホールアイテムが一定時間経つと画面最下段まで移動(スクロール)する

その他

ゲームプレイ自体に直接影響するわけではないが、不可解な事象たちや特徴的なゲームシステムなど

  • 微妙に音痴で短いフレーズが延々ループする BGM
  • 毒々しい配色・デザインの敵たち
  • 他の弾と見分けがつかないデザイン・弾道からの突然変化球する敵弾
  • プレイヤーは「POLLUX と戦うこと」を命ぜられている
  • 一定距離にある敵弾を吸い込むブラックホールが時々登場する
    • 吸い込まれて軌道が変わった敵弾に自機が当たっても平気

メインショットをパワーアップしたり 3way にすると、サブウエポンのミサイルの発射数が目に見えて少なくなる (発射間隔が長くなる)

  • ノーマル + ミサイル + ソフトウェア連射
    • メインショットが画面上に4発(|| で一発と数えて) + ミサイルが8発 (4組)
      f:id:minosjp:20191231192208p:plain
      ノーマル + ミサイル + ソフトウェア連射
  • 3way + ミサイル + ソフトウェア連射
    • メインショットが画面上に11 or 12発(|| で一発と数えて) + ミサイルが3 or 4発 (2組) 出ている
      f:id:minosjp:20191231195424p:plain
      3way + ミサイル + ソフトウェア連射 その1
  • 上記2つは敵の有無など条件が違うので正確な比較ではなく以下妄想であることは最初に断っておく
    • メインショットがノーマルのときは、連射に合わせて正しく弾が出ているように見える
    • メインショットが 3way のときは、メインショットは連射に合わせて正しく出ているように見えるが、ミサイルは連射2回につき1回しか出ていないように見える
    • 上記以外の 3way 時にはミサイルの出がもっと悪いときもあるように見える
    • 何が「ミサイルの出」を制約しているのか…
    • 次の画像は連射に対してメインショットもミサイルも出ていない瞬間があるように見える (自機の真ん前にある自弾のさらに前の弾との間隔が空いてる)
      f:id:minosjp:20191231202548p:plain
      3way + ミサイル + ソフトウェア連射 その2
    • メインショットとミサイルをあわせた画面内にある自機の弾数に上限があるのでは…
      • 優先的に出るのはメインショット…??
      • ゲームフレームごとにやってるだろう発射処理擬似コードで書くとこんな感じか…
if (ショットボタンが release から press 状態になった) {
  if (画面内の自弾総数が制限以内) {
    メインショット発射処理
  }
  if (ミサイル装備中 かつ 画面内の自弾総数が制限以内) {
    ミサイル発射処理
  }
}

敵や敵弾の多い場面で1P側のボム発射ボタンおよび2P側のレバー右が効かなくなる

  • 処理落ちで処理がすっ飛ばされるにしても「レバーの特定方向のみが効かない」というのは不可解だし、 1P / 2P で症状が違うのも不可解
  • 普通のSTGで「処理落ち」というとゲーム自体のフレームレートが下がって露骨にスローがかかっているような状態になるが、ポラックスはそういう感じがない
    • 規定のフレームレートに処理が間に合わなくなるとそのフレームのやりかけてる処理を全部やめてる??
  • 1P のボム発射ボタンと 2P のレバー右の症状が紐付いているのも容易な説明は思いつかない
    • GPIO とかが足りなくて ADC 使って入力を受け付けていて 1P のボム発射ボタンと 2P のレバー右が同じ入力系統になっていて過負荷で処理が打ち切られてすっ飛ばされて効かないというのは考えられるが、1P のボム発射ボタンと 2P のレバー右を同じ系統にする理由がまるで思いつかない

ハードウェア連射装置を使うと、敵が多くいる場面で自機の弾が出なくなる

  • もし、フレームレートに間に合わなくなると処理を打ち切っているせい、だとすると、レバー移動すら効かなくなったりしてもおかしくないのだがそういう挙動はなさそう…
  • ミカドさんのハードウェア連射装置は、動画中で「15連」「30連」という言葉が聞かれるように、いわゆる「シンクロ連射」だろうから VSYNC に同期しているはず
    • 読み取る側の処理が VSYNC に同期してない?

ボス敵が突然固くなり数分間攻撃を当て続けても倒せなくなる (普段はもちろんもっと短い)

  • ヒット判定で光っているようには見える
  • 「光らせるところまでは処理入れてるのに、ダメージ処理までは入らず打ち切ってる」なんてことはとても考えられない
  • ミカドさんの攻略動画は「ミサイル出なくなる」対策のため基本的にあまりパワーアップせずにボス戦に挑んでる
    • テストはパワーアップしてる状態でしかやってなくて、小さいダメージの積み重ねが正しく入らない状態になることがある?

ブラックホールアイテムが一定時間経つと画面最下段まで移動(スクロール)する

  • これこそ妥当な説明が思いつかないやつ…
  • 「画面上の絶対位置にとどまるはずのものが急に動き出す」「最下段にはとどまり続ける」という2点に分解して考えるのが良いか…??
  • 「画面上の絶対位置にとどまるはずのものが急に動き出す」
  • スクリプトなのかデータなのかわからんが、とどまる時間を有限でしか表現できないようになっていて、とりあえず最長にしてあるとかだろうか…
  • 「最下段にはとどまり続ける」
    • 自発的な移動以外では画面外に出られない (fail safe 的なコードで何となく画面下端からフレームアウト出来ずにとどまり続けている、みたいな…)

結局…

いろいろ考えてみたんだけど、どんなポカをやったらあの挙動のまま世に出てしまうほどのものが作れるのかは分からんかった(滝汗)。 ただ考えてみた結果ちょっと思い当たったのは「一般的な同期システムに則らずにゲームの1フレームを処理してるんじゃないか?」ということ。

  • 同期システムが厳密な周期を刻んでいない
  • その同期システムの中で規定している1フレーム内に処理が間に合わなかった時は、フレーム内の処理を適当に途中で打ち切ってる

とすると、何となく説明がつきそうなことが多いんだよな…。 とはいえ、これも妄想ですけどw

short の複合代入演算

-Werror=conversion  とかコンパイルオプション厳しくしちゃうと

    short n = 1;
    const short m = 2;
    
    n += m;

って通らないの!?

prog.cc: In function 'int main()':
prog.cc:10:7: error: conversion from 'int' to 'short int' may change value [-Werror=conversion]
   10 |     n += m;
      |     ~~^~~~

Implicit integer promotion 後の代入が許せないってこと?? ぐぬぬぬ…。