monoの開発ブログ

HSP3 ショートプログラミングTips その3

過去記事の宣伝を繰り返しているのもアレなので、そろそろ新作投下します。あんまりネタがないのでとりあえず2つだけ。

当たり判定 (-12bytes)

改善前 (78bytes)

#const RA 11
#const RB 20

#const R (RA + RB)
#const R2 (R * R)

dx = ax - bx
dy = ay - by
if dx * dx + dy * dy <= R2 {
  // ...
}

改善後 (66bytes)

#const RA 11
#const RB 20

#const R (RA + RB)

if (abs(ax - bx) | abs(ay - by)) <= R {
  // ...
}

解説

改善前は円形での判定、改善後は正方形での判定になっていてそもそも全然違うコードですが、気にしたら負けです。それっぽく当たり判定ができれば形状なんてどうでもいいのです。

円形で判定する場合、2点の距離を出すために2乗を計算する必要があります。そのために変数への代入が生じ、容量が膨らんでしまいます。改善後のコードはabs関数の引数部分に一度書くだけでよいので、変数代入を省くことができます。一般に関数呼び出しはコストが大きいので避けたいところですが、使い方によっては逆転することもあるのです。

容量削減の理由は説明しましたが、そもそも改善後のコードがなぜこれで当たり判定できているのか分かりにくいかもしれません。この判定方法はいつでも使えるわけではなく、Rの値が2n-1のときだけ使えます。X方向の距離とY方向の距離のうち、両方がnビットの範囲内に収まれば当たっていると判断します。

また、改善前のコードにも利点はあります。同時に2点間の角度も求めたい場合にはatan(dy, dx)と記述できるので、サイズが逆転するのです。臨機応変に適切な書き方を使いましょう。

obaqでのオブジェクトの検索 (-70bytes)

改善前 (349bytes)

#include "obaq.as"

#const GROUP_A 0x0001
#const GROUP_B 0x0002
#const GROUP_C 0x0004

qfind
*@
  qnext id
  if id >= 0 {
    qgetgroup id, group, exgroup, loggroup
    if group == GROUP_A {
      // GROUP_A
    }
    else : if group == GROUP_B {
      // GROUP_B
    }
    else {
      // GROUP_C
    }
    goto *@b
  }

改善後 その1 (303bytes)

#include "obaq.as"

#const GROUP_A 0x0001
#const GROUP_B 0x0002
#const GROUP_C 0x0004

qfind GROUP_A
*@
  qnext id
  if id >= 0 {
    // ...
    goto *@b
  }

qfind GROUP_B
*@
  qnext id
  if id >= 0 {
    // ...
    goto *@b
  }

qfind GROUP_C
*@
  qnext id
  if id >= 0 {
    // ...
    goto *@b
  }

改善後 その2 (279bytes)

#include "obaq.as"

#const GROUP_A 0x0001
#const GROUP_B 0x0002
#const GROUP_C 0x0004

repeat 3
  qfind 1 << cnt
  *@
    qnext id
    if id >= 0 {
      if cnt {
        if cnt == 2 {
          // GROUP_C
        }
        else {
          // GROUP_B
        }
      }
      else {
        // GROUP_A
      }
      goto *@b
    }
loop

解説

改善前のコードは一見効率が良さそうですが、致命的な問題があります。それは、qgetgroup命令を使っていることです。拡張プラグインの命令は、コード中で一度でも利用するとその定義が組み込まれます。そのため、利用する命令の種類が増えると容量が大幅に増加し、グループを指定したqfindを単純に3回記述しているだけの改善後 その1にすら劣ってしまいます。改善後 その2では3回記述していた部分をループにすることで順当に削っています。