著者:飯尾淳
本連載の第5 回(Vol.51、2017 年12 月号)で、バーティカルバー(垂直棒)が3 本立っているという理由から「ハノイの塔」というパズルを取り上げました。今回は、そのときの考察を思い出しつつ、ハノイの塔を解くプログラムを再び考えます。
ただし、今回は三つの塔を「上から見下ろした」状態、俯瞰(ふかん)で考えます。至ってシンプルなルールで解ける面白さを、動作の可視化プログラムを用いて確認してみましょう。
シェルスクリプトマガジン Vol.60は以下のリンク先でご購入できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
const COLORS = [ 'crimson', 'forestgreen', 'yellow', 'royalblue', 'saddlebrown', 'hotpink', 'darkorange', 'darkmagenta' ]; const N_DISKS = COLORS.length; const BASE_LENGTH = 200; const C_WIDTH = 3.732 * BASE_LENGTH; const C_HEIGHT = 3.500 * BASE_LENGTH; const DISK_R = 0.9 * BASE_LENGTH; const POLE_R = 15; const POSITIONS = { 'Source' : [0.268, 0.714], 'Auxiliary' : [0.500, 0.286], 'Destination' : [0.732, 0.714] }; class Position { constructor(x, y) { this.x = x; this.y = y; } } class Disk { constructor(level) { this.level = level; this.color = COLORS[level]; this.r = (DISK_R-POLE_R)*(N_DISKS-level) / N_DISKS + POLE_R; } } class Tower { constructor(name, disks, direction=null) { this.name = name; this.disks = []; for (var i = 0; i < disks; i++) { this.disks.push(new Disk(i)); } this.direction = direction; var rx, ry; [rx,ry] = POSITIONS[name]; this.pos = new Position(rx*C_WIDTH, ry*C_HEIGHT); } } var src = new Tower('Source', N_DISKS); var aux = new Tower('Auxiliary', 0, src); var dst = new Tower('Destination', 0, src); // 円盤の数(N_DISKS)が奇数のときはDestinationを、 // そうでないときはAuxiliaryを向くようにする src.direction = (N_DISKS % 2 == 1) ? dst : aux; function setup() { createCanvas(C_WIDTH, C_HEIGHT); frameRate(30); } function draw() { // put drawing code here } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
class Disk { constructor(level) { (略) } draw(pos) { stroke('black'); fill(this.color); ellipse(pos.x, pos.y, 2*this.r); } } class Tower { constructor(name, disks, direction=null) { (略) } draw() { var pos = this.pos; var pos2 = this.direction.pos; var sx, sy, dx, dy, r; // 円盤を描く this.disks.forEach(function(d) { d.draw(pos) }) // 支柱を描く stroke('brown'); fill('white'); ellipse(pos.x, pos.y, 2*POLE_R); // 向きを描く stroke('navy'); [sx, sy] = [pos.x, pos.y ]; [dx, dy] = [pos2.x, pos2.y]; r = POLE_R / Math.sqrt((dx-sx)*(dx-sx)+(dy-sy)*(dy-sy)); [dx, dy] = [(dx-sx)*r+sx, (dy-sy)*r+sy]; line(sx, sy, dx, dy); } } (略) function setup() { createCanvas(C_WIDTH, C_HEIGHT); frameRate(30); } function draw() { background('beige'); [src, aux, dst].forEach(function(t) { t.draw(); }) } |