Minecraftとタートルと僕

PCゲームMinecraftのMOD「ComputerCraft」の情報を集めたニッチなブログです。

APIを作ろう(3)-Colors API を調べてコツをつかもう

はじめに

前回は失礼しました。

それでは新しいAPIを実装するために、既存のAPIを調べてみましょう。

「Craft OS」「Turtle OS」が提供しているAPIをいろいろ覗いてみたのですが、
小さくてシンプルという点でColors APIが参考になりそうです。

今回はこのColors APIを調べてみましょう。
なお、このAPIの詳細については以下を参照してください。
CC非公式Wiki Colors API

また、いつものごとく長いので、時間のない人は「まとめ」だけ見て下さい。

Colors APIとは

これは何をするAPIなのか

色または色集合(白と青と黒、のように単純に色を羅列した集合)を取り扱うためのもので、
MFRのRedNetCableやRedPowerのBundledCable、そして金色のAdvanced Computer(Monitor)などで利用します。
イギリス人向けに別綴りの同一API(Colours API)も用意しているところが気配り細やかですね。

なおCCで扱える色は全部で16色。以下のように仕様が決まっています。

  • 16ビット2進数において1か所だけ1を立てたもの16種類。これを使って16色を表現
  • 10進数表現で言うならば、白=1(2の0乗)、オレンジ=2(2の1乗)、・・・、黒=32768(2の15乗)、のように対応している。
  • これによって色集合を、各色のビット論理和を取ることで表現できる。

最低限これをだけ知っておけば大丈夫

2進数とか言われましても・・・という方へ。
2進数わからなくても大丈夫。ビット演算とかわからない人でも色を取り扱えるようにしてくれるのがこのAPIの目的です。とても親切!
以下の赤字だけ覚えてください。シンプルなAPIでしょ? これこそがまさにAPIの本質ですね。

  • プログラム中で使える色は以下の16色。
    • white, orange, magenta, lightBlue, yellow, lime, pink, gray, lightGray, cyan, purple, blue, brown, green, red, black
  • 色を指定するためには、たとえば白なら、「colors.white」という定数を使う。
  • MFRのRedNetCableでは、「白と青と黄色」のように複数の色を一度に指定したいことがある。
    • そのときには、colors.combine関数を使って、以下のように色集合を作り、指定する。
colors.combine(colors.white, colors.blue, colors.yellow)

Colors APIの実装

概要

  • まず、グローバル変数「colors」が用意される。この中身はテーブル。
  • このテーブルの中に、16個の色定数、色集合を扱うための3つの関数が入っている
  • 「colors.white」のように「.」(ドット)記法で各定数、関数にアクセスする。

具体的な実装

/rom/program/apis/colors というファイルがその実装です。
以下に引用したのでざっと眺めてみましょう。

-- Colors
white = 1
orange = 2
magenta = 4
lightBlue = 8
yellow = 16
lime = 32
pink = 64
gray = 128
lightGray = 256
cyan = 512
purple = 1024
blue = 2048
brown = 4096
green = 8192
red = 16384
black = 32768

function combine( ... )
  local r = 0
  for n,c in ipairs( { ... } ) do
    r = bit.bor(r,c)
  end
  return r
end

function subtract( colors, ... )
	local r = colors
	for n,c in ipairs( { ... } ) do
		r = bit.band(r, bit.bnot(c))
	end
	return r
end

function test( colors, color )
  return ((bit.band(colors, color)) == color)
end
  • 前半は16個の定数定義。
  • 後半の関数定義では、ビット演算を駆使している。2進数苦手な人も多いだろうしここでは詳細には触れません。
  • 関数定義時の引数として「...」を記述していますが、これは可変引数(引数の個数はいくつでも可)に対応させるためです。
  • ポイントは、定数も、関数もすべてグローバルとして定義していること。

つまり、「colors」というファイルの中にグローバルとして変数(定数)・関数を定義しておくと、他のプログラムはそれらを「colors」というグローバル変数を介して利用できるようになる。

なお補足ですが、ファイル内でlocal宣言してローカルで定義した変数・関数は、プログラマ側は利用できません。
また、標準のAPIではなく新しく追加されたAPIを利用するには、「os.loadAPI(ファイル名)」関数を使う必要があります。

まとめ: API作成時のポイント

以上をまとめます。

CCコンピュータにおけるAPI

「Turtle OS」「Craft OS」のAPIは、実用上、以下のように提供されています。

  • APIに含まれる定数・関数は、一つのグローバル変数(たとえば「colors」)の中にテーブルの形で入っています。
  • このテーブルの中にある定数・関数には、「colors.white」「colors.combine()」のように「.」(ドット)記法でアクセスします。

APIの実装方法

そのため、新しいAPIを実装するためには以下のことに気を付けます

  • 「定数・関数を格納するグローバル変数名」=「ファイル名」です。
    • つまり「myapi」というファイル内で定義した関数「hevohevo()」は、「myapi.hevohevo()」と記述することで利用できる。
  • このファイル中でグローバル宣言した変数・関数は、すべて外部プログラムから利用可能になります。
  • 外部プログラムからアクセスして欲しくないものは、すべてローカル宣言しておきましょう。

APIの利用宣言

APIを外部のプログラムで利用するためには次のいずれかの方法をとります。

方法1:os.loadAPI()関数をプログラムの先頭で使う

新しく作った自家製APIを利用するためには、
利用する側のプログラムの最初の方で、OS APIのloadAPI()関数を使いましょう。

os.loadAPI("myapi")  --プログラムと同じディレクトリにあるならファイル名だけで良い
os.loadAPI("/rom/apis/opt/myapi")  -- ちがうディレクトリにあるなら絶対パス表現で

この関数が、ファイル名と同じ変数作ってファイル内のグローバル変数・関数を格納、というところまで一気にやってくれるわけですね。ありがたやー。

方法2:標準APIとして読み込まれるディレクトリにAPIファイルを保存する。
  • 「/rom/apis/」が標準のディレクトリです。タートル用APIなら「/rom/apis/turtle」です。
  • ここにファイルを保存しましょう。
  • あとは自動的にloadAPI()してくれます(なおその作業はbios.luaファイル内に記述されています)。
方法3:スタートアップファイルで自動的にloadAPIさせる
  • 自作APIをloadAPI()するプログラム(スクリプト)を作っておいて、CCコンピュータ起動時に実行させるのもよいでしょう。
  • 「/rom/autorun/」ディレクトリの利用ですね。具体的なスクリプト、やり方についてはまた後日。