プログラミング はじめの一歩 JavaScript + p5.js編
26:どう組み合わせればいい?

この記事の詳しい内容には四国新聞 WEB朝刊の「どう組み合わせればいい?」ページから有料記事に進むことで読めます。

概要

かめのロボットを動かして線を引きます。ロボットには次の4つの命令を出すことができます。

命令:

  • 進む(n) nマス分進む
  • 左回り() その場で左を向く
  • 右回り() その場で右を向く
  • ここから m回繰り返す ここまで 台形で囲んだ命令をm回繰り返す

さてここで問題です。ロボットが下図のような線を引くには、下のフローチャートの空欄ア、イ、ウにどんな数と命令を入れればよいでしょう?

論理を考える

フローチャートを日本語の疑似コードで表すと、

(ア)回繰り返し
	進む(1)
	(イ)
	進む(1)
	(ウ)
繰り返しここまで

のようになります。

頭の中だけで考えるのは難しいので、絵を描いてみましょう。下図の(1)は進む(1)、(2)は(イ)、(3)は進む(1)、(4)は(ウ)です。(5)、(6)、(7)、(8)は(1)から(4)の繰り返しです。

ということは、(ア)は数値2で、(イ)は命令:左回り()、(ウ)は命令:右回り() です。これが問題の答えです。コードにすると、次のようになります。

for (let i = 0; i < 2; i++) {
    kame.goForward(1);
    kame.turnToLeft();
    kame.goForward(1);
    kame.turnToRight();
}
全コード

以下は、前の「25:くり返しの中にくり返し」で扱った、指定時間だけ待つPromiseを使った全コードです。

const rows = 3; // 横向きの行数
const columns = 3; // 縦向きの列数
const gutter = 0; // 矩形間の空き
const w = 50; // 矩形の幅
const h = 50; // 矩形の高さ
// 矩形の開始位置をずらす量 => (offsetX,offsetY)から始まる
const offsetX = 60;
const offsetY = 60;

let kame, kameImages;
let isAction = false;

function preload() {
    // かめの右向き、下向き、左向き、上向きの画像を読み込み、
    // そのイメージを右、下、左、上の順に配列に入れる
    const right = loadImage('images/right.png');
    const down = loadImage('images/down.png');
    const left = loadImage('images/left.png');
    const up = loadImage('images/up.png');
    kameImages = [right, down, left, up];
}

function setup() {
    createCanvas(300, 300);
    // イメージの基点をイメージセンターにするモード
    imageMode(CENTER);
    // Kameインスタンスを作成
    kame = new Kame(60, 160, kameImages);
}

function mousePressed() {
    if (!isAction) {
        kameAction();
        isAction = true;
    }
}

function waitUntill(ms) {
    const promise = new Promise(executor);
    // Promiseのコンストラクタに渡すexecutor()関数
    function executor(resolve, reject) {
            if (isNaN(ms)) {
                reject('数値でない');
            }
            else {
                // ms秒まったら解決
                window.setTimeout(() => {
                    resolve('成功');
                }, ms);
            }
        }
        // というPromiseオブジェクトを返す
    return promise;
}

// かめを動かす
async function kameAction() {
    // 2回繰り返す
    for (let i = 0; i < 2; i++) {
        kame.goForward(1);
        await waitUntill(1000); // 1000ミリ秒待つ
        kame.turnToLeft();
        await waitUntill(1000); // 1000ミリ秒待つ
        kame.goForward(1);
        await waitUntill(1000); // 1000ミリ秒待つ
        kame.turnToRight();
        await waitUntill(1000); // 1000ミリ秒待つ
    }
}

function draw() {
    background(220);
    // グリッドの線の太さ
    strokeWeight(1);
    // 矩形を格子状に並べて描く
    for (let c = 0; c < columns; c++) {
        for (let r = 0; r < rows; r++)
        // 行数(rows) x 列数(columns)に矩形を描く
            rect(offsetX + c * (gutter + w), offsetY + r * (gutter + h), w, h);
    }
    // カメが引く線の太さ
    strokeWeight(7);
    kame.drawLine() // カメが線を引く
    kame.display(); // カメを描画
}

class Kame {
    constructor(x, y, imgs) {
            this.x = x;
            this.y = y;
            // 向いている向きの配列 [右、下、左、上]の順番
            this.directions = ['right', 'down', 'left', 'up'];
            // 現在の方向を数値で表す
            this.currentDirection = 0;
            // 現在の方向を文字列で表す
            this.direction = this.directions[this.currentDirection];
            // カメのイメージ
            this.images = imgs;
            // 向きによってイメージを変える
            this.image = this.images[this.currentDirection];
            // カメの1歩は50ピクセル = グリッドの正方形の一辺の長さと同じ
            this.step = 50;
            // 移動する座標を保持する配列
            this.prevousXs = [];
            this.prevousYs = [];
            // 最初の位置を追加する
            this.prevousXs.push(this.x);
            this.prevousYs.push(this.y);
        }
        // カメのイメージを描画する
    display() {
            image(this.image, this.x, this.y);
        }
        // 右を向く
    turnToRight() {
            this.currentDirection++;
            // 循環させる
            if (this.currentDirection >= 4) {
                this.currentDirection = 0;
            }
            // カメの新しい向き
            this.direction = this.directions[this.currentDirection];
            // カメの新しいイメージ
            this.image = this.images[this.currentDirection];
        }
        // 左を向く
    turnToLeft() {
            this.currentDirection--;
            if (this.currentDirection <= -1) {
                this.currentDirection = 3;
            }
            this.direction = this.directions[this.currentDirection];
            this.image = this.images[this.currentDirection];
        }
        // stepsマス分だけ前進
    goForward(steps) {
            // 前進と言っても、今の向きによって方向は変わる
            // 今の向きが右なら
            if (this.direction === 'right') {
                // 右にstepsマス分(50 * steps ピクセル)移動
                this.x += this.step * steps;
            }
            else if (this.direction === 'down') {
                this.y += this.step * steps;
            }
            else if (this.direction === 'left') {
                this.x -= this.step * steps;
            }
            else if (this.direction === 'up') {
                this.y -= this.step * steps;
            }
            // 新しいxy位置を配列に追加
            this.prevousXs.push(this.x);
            this.prevousYs.push(this.y);
        }
        // 通ってきた位置を結ぶ線を引く
    drawLine() {
        for (let i = 0; i < this.prevousXs.length; i++) {
            line(this.prevousXs[i], this.prevousYs[i], this.prevousXs[i - 1], this.prevousYs[i - 1]);
        }
    }
}

下の画面をクリックすると、かめのロボットが移動しお題通りの線を引きます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA