ATMega168(8MHz)を使ったJapaninoでこの数のLEDを常時点灯させるのはタイミング的にはかなりきびしく、個々の信号線をdigitalWrite関数で制御したのでは間に合わず、表示がちらついてしまいます(digitalWrite関数は、内部でいろいろなチェックや操作を行っているので時間がかかるのです)。そこでこの例では、ポートに直接バイトデータを書き込むという、あまり好ましくないやり方でLEDパネルを制御しています(16MHzなら、digitalWrite関数でも、ほとんどちらつきのない表示が得られます)。
使用する円盤の数は1枚から10枚で、可変抵抗を使い、電圧変化をアナログポートで読み込んで枚数を決めています。最初はスイッチを押した回数で円盤の数を決めていたのですが、4個のモジュールすべてにデータ信号を送れるように配線した結果、スイッチ用のポートがなくなってしまったのです。
さて、肝心のハノイの塔のアルゴリズムですが、わずかこれだけです。
void hanoi(int src, int dst, int tmp, int cnt)
{
if (cnt > 0) {
hanoi(src, tmp, dst, cnt - 1);
moveDisc(src, dst);
hanoi(tmp, dst, src, cnt - 1);
}
return;
}
この関数には、元の柱、移動先の柱、もう1本の柱の番号と、移動する円盤の数を渡します。この関数の中で行っていることは簡単です。
- 指定した枚数より1枚少ない円盤をもう1本の柱に移す。
- 残った1枚の円盤を移動先の柱に移す。
- もう1本の柱の円盤を移動先の柱に移す。
要点は、指定された枚数より1枚少ない円盤の移動のために、このhanoi関数自身を再び呼び出しているということです。このような呼び出しを、再帰呼び出しといいます。
だまされたような気がしますが、これでちゃんとパズルを解くことができるのです。例えば、3枚の円盤を移動するのであれば、最初に2枚を移動し、一番大きい1枚を移動先に移し、その上に先ほどの2枚を戻すということになります。この2枚の移動は、移動元、移動先、もう1本の柱の割り当てと円盤の枚数が異なる形で、同じ関数を呼び出しています。何度もこのような呼び出しを繰り返し、最後に1枚の移動になれば、普通に移動するだけです。
プログラミングの勉強では、再帰呼び出しの例としてハノイの塔の解法アルゴリズムがしばしば取り上げられています。
プログラムでは、ある柱から別の柱への円盤の移動をアニメーション表示するために、、上方向、水平方向、下方向に、1ドットずつ位置を変えながら表示しています。そして1ドット動かした後に表示ルーチンを呼び出しています(表示ルーチンの呼び出しが止まると、表示が消えてしまいます)。
LEDパネルは消費電力がかなり大きいので、Japanino側からは供給できないので、専用の電源を用意し駆動しています。作例では5V10A(50W)の電源を使用しています。Japaninoの基板は、作例ではUSB、あるいはバッテリーボックスから供給していますが、LED電源で駆動することもできます。別電源を使用する場合は、LED用電源とJapanino用電源の+側を接続してはいけません(GNDどうしは接続しておく必要があります)。 |