プログラミング はじめの一歩 JavaScript + p5.js編
24:魚をつるロボット

この記事の詳しい内容には毎日新聞の「魚の取りすぎをふせぐ」ページから有料記事に進むことで読めます。

概要

魚つりをするロボットがいます。このロボットは、魚の取りすぎをふせぐため、ルールにしたがってつった魚を海に返します。
ルール:

  • つった魚が1ぴきだけの日は、海に返さずに持ち帰る
  • つった魚が2ひき以上だったときは、1ぴきを海に返して残りを持ち帰る

たとえば、4ひきつった日にには、1ぴきを海に返し、残りの3びきを持ち帰ります。

ここで問題です。ロボットは5日間で次の数だけ魚をつりました。持ち帰った魚の数は合わせて何びきになるでしょう?

1日め 2日め 3日め 4日め 5日め
2ひき 2ひき 3ひき 1ひき 1ひき
論理を考える

ルールを見ながら日別に計算すると、1 + 1 + 2 + 1 + 1 = 6 となり、答えは6ぴきです。

疑似コード

ルールを日本語の疑似コードで置き換えると、次のようになります。

持ち帰る魚の数 = 0;

もしつった魚の数が1なら、
	持ち帰る魚の数 = 1
そうでなく、 つった魚の数が2以上なら
	持ち帰る魚の数 = つった魚の数 - 1

簡易的なコードでは次のように記述できます。

fishNum = 3 // つった魚の数
catchNum = 0 // 持ち帰る魚の数、最初は0
releaseNum = 1	// 海に返す魚の数はつねに1
if (fishNum === 1) {
    catchNum = 1
}
else if (fishNum >= 2) {
    catchNum = fishNum - releaseNum
}

つった魚の数は変化するので、ルールを関数化し、つった魚の数をパラメータで定義すると汎用的になります。

関数化

次のコードは関数の例です。この関数は、つった魚の数をパラメータで受け取り、持ち帰る魚の数を返します。

// つったったさかなの数から海に返す数と持ち帰る数を計算し、
// 持ち帰る数を返す
function catchAndRelease(fishNum) {
    print('つった数: ' + fishNum);
    const releaseNum = 1; // 海に返す数はつねに1
    let catchNum = 0; // 持ち帰る数、最初は0
    // つった数が1なら
    if (fishNum === 1) {
        // それは持ち帰る数
        catchNum = 1;
        // そうでなく、つった数が2以上なら
    }
    else if (fishNum >= 2) {
        print('海に返す数: ' + releaseNum);
        // 持ち帰る数はつった数から海に返す数を引いた数
        catchNum = fishNum - releaseNum;
    }
    print('持ち帰る数: ' + catchNum);
    print('---------------');
    // 持ち帰る数を返す
    return catchNum;
}

この関数は次のように使用できます。この関数はその日につった魚の数を引数にとり、そのたびに持ち帰る魚の数を返すので、前もって0で初期化したlet 変数を用意しておいて、それに足していきます。


let totalCatch =0;

totalCatch += catchAndRelease(2); // 1日め 2ひきつった
totalCatch += catchAndRelease(2); // 2日め 2ひきつった
totalCatch += catchAndRelease(3); // 3日め 3ひきつった
totalCatch += catchAndRelease(1); // 4日め 1ひきつった
totalCatch += catchAndRelease(1); // 5日め 1ひきつった
print('持ち帰った数の合計' + totalCatch);

上記コードを実行すると、[コンソール]に下図の結果が表示されます。

これで論理は完成です。

視覚化

ロボットや魚のイメージをロードし、視覚化します。次のコードは、画面のマウスプレスでスタートするプログラムの例です。

let coolerBox = [];
let fishImage, robotImage;
let isStart = false;
// 日別のつったさかなの数
let fishNumList = [2, 2, 3, 1, 1];
// 持ち帰りの合計
let total = 0;

function preload() {
    robotImage = loadImage('images/robot.png');
    fishImage = loadImage('images/fish.png');
}

function setup() {
    createCanvas(400, 300);
    noStroke();
}

// 画面のマウスプレスでスタート
function mousePressed() {
    if (!isStart) {
        isStart = true;
        let cnt = 0; // catchAndRelease()の呼び出し回数をカウント
        let timerID;
        if (isStart) {
            timerID = window.setInterval(() => {
                if (cnt < fishNumList.length) {
                    // 持ち帰りの数を配列に追加 => [1,1,2,1,1]
                    coolerBox.push(catchAndRelease(fishNumList[cnt]));
                    // 配列要素の値を合計 1+1+2+1+1 = 6
                    total = coolerBox.reduce((sum, element) => {
                        return sum + element;
                    }, 0);
                }
                else {
                    window.clearInterval(timerID);
                }
                cnt++;
            }, 2000);
        }
    }

}

function draw() {
    background(229, 236, 185);
    // イメージを描画
    image(robotImage, 100, 150);
    // 手前の海を描画
    fill(113, 138, 194);
    rect(0, 270, width, 30);
    // 奥の台を描画
    fill(170, 140, 94);
    rect(20, 70, 250, 30);
    // 持ち帰り合計分だけさかなを描画
    for (let i = 0; i < total; i++) {
        image(fishImage, 30 + 40 * i, 50);
    }
}

function catchAndRelease(fishNum) {
    let catchNum = 0;
    const releaseNum = 1;
    if (fishNum === 1) {
        catchNum = 1;
    }
    else if (fishNum >= 2) {
        catchNum = fishNum - releaseNum;
    }
    return catchNum;
}

画面のマウスプレスで、持ち帰る魚が台の上に描画されます。

コメントを残す

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

CAPTCHA