プログラミング はじめの一歩 JavaScript + p5.js編
31:2色のカードで数を表す

この記事の詳しい内容には毎日新聞の「2色のカードで数を表す」ページから有料記事に進むことで読めます。

概要

カードを4枚使って、数を表します。カードは黒と白の2色です。表す数は次のルールにしたがって決まります。

  • カードは左から1枚ずつ、8,4,2,1の数を表す
  • 黒いカードの数を足す

たとえば、黒、白、白、黒の場合には、8と1のカード黒のカードなので、8 + 1 = 9 を表すことになります。

ここで問題です。13を表しているのは、次の(1)から(4)のうちのどれでしょう?

手作業で考える

かたい頭ではなかなか論理を思いつかないので、ルールに慣れる意味も含めて、紙と鉛筆を使って考えてみましょう。四角を4つ並べて書いて、黒い四角を黒く塗ります。下に点数(ポイント)を書いて、足すと、下図のようになります。このことから、13を表すのは(3)だと分かります。

論理を考える

このお題の要点は、白と黒の矩形の並びがあって、並びの、左からの位置によってその矩形のポイントが決まり、黒だけを加算する点です。

文字列で考えてみる

矩形の並びがあって、その位置によって矩形のポイントが決まるということなので、配列が使えそうに思えます。しかし収める要素をどうすればよいか分からないので、まずは文字列で表してみることにします。

// [黒、白、白、黒]を表す
let arr = ['黒', '白', '白', '黒'];

そして、この配列の要素をforループで調べて、’黒’であるときには、その要素のインデックス位置に応じて、8か4か2か1をその要素のポイントとして、加算します。ポイントは加算していくので、合計をメモしつづける専用の変数がいります。

// ポイントを足して合計するための変数
let total = 0;
// 配列の要素数は4つと決まっている
for (let i = 0; i < 4; i++) {
    // 配列に含まれる文字列を調べる
    const colStr = arr[i];
    // 合計するのは黒の場合だけ
    if (colStr === '黒') {
        // 配列のインデックス位置によって、足す数値を変える
        if (i === 0) {
            total += 8; // 一番左は8ポイント
        }
        else if (i === 1) {
            total += 4; // 2番めは4ポイント
        }
        else if (i === 2) {
            total += 2; // 3番めは2ポイント
        }
        else if (i === 3) {
            total += 1; // 一番右は1ポイント
        }
    }
}
// 合計を出力して確認
print(total);

変数arrに割り当てる配列を、お題の(1)->(4)に適宜割り当てると、白黒カードの組み合わせで表される数字が出力できます。

// [黒、白、白、黒]を表す
let arr = ['黒', '白', '白', '黒'];
// [黒、黒、黒、白]を表す
arr = ['黒', '黒', '黒', '白'];
// [黒、白、黒、黒]を表す
arr = ['黒', '白', '黒', '黒'];
// [黒、黒、白、黒]を表す
arr = ['黒', '黒', '白', '黒'];
// [黒、白、黒、白]を表す
arr = ['黒', '白', '黒', '白'];

[‘黒’, ‘黒’, ‘白’, ‘黒’]のとき13が出力されるので、答えは(3)だと分かります。

文字列を数値に置き換えて、矩形の並びを描画する

人間にとって’黒’は黒色、’白’は白色だと分かりますが、コンピュータにはどちらも文字列です。カードの矩形を描画したい場合には、文字列の’黒’と’白’を数値に置き換えると、塗り色を設定するfill()関数にそのまま渡すことができます。

* p5.jsのfill()関数は文字列でも、英語の’black’や’white’なら黒、白と判断できます。

fill()にそのまま渡せるようにするには、黒を0、白を255にします。

// [黒、白、白、黒]を表す
let arr = [0, 255, 255, 0];

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

function setup() {
    createCanvas(400, 300);
    background(220);
    // [黒、白、白、黒]を表す
    let arr = [0, 255, 255, 0];
    // [黒、黒、黒、白]を表す
    arr = [0, 0, 0, 255];
    // [黒、白、黒、黒]を表す
    arr = [0, 255, 0, 0];
    // [黒、黒、白、黒]を表す
    arr = [0, 0, 255, 0];
    // [黒、白、黒、白]を表す
    // arr = [0, 255, 0, 255];

    // ポイントを足して合計するための変数
    let total = 0;
    // 配列の要素数は4つと決まっている
    for (let i = 0; i < 4; i++) {
        // 配列に含まれる文字列を調べる
        const colNum = arr[i];
        // 合計するのは黒の場合だけ
        if (colNum === 0) {
            // 配列のインデックス位置によって、足す数値を変える
            if (i === 0) {
                total += 8; // 一番左は8ポイント
            }
            else if (i === 1) {
                total += 4; // 2番めは4ポイント
            }
            else if (i === 2) {
                total += 2; // 3番めは2ポイント
            }
            else if (i === 3) {
                total += 1; // 一番右は1ポイント
            }
        }
    }
    // 合計を出力して確認
    print(total);

    // 格子ブロックのひな型コード
    // 1 x 4 の矩形を描画
    for (let i = 0; i < 4; i++) {
        const col = arr[i];
        fill(col);
        rect(10 + i * (5 + 30), 10, 30, 30);
    }
}

変数arrが[0, 0, 255, 0]のときには、下図に示すように、矩形は黒、黒、白、黒の並びで描かれ、[コンソール]に13が出力されます。

矩形を並べて描くときには、「格子ブロックのひな型コード」が役に立ちます。このお題では横の並びだけを描くので、このコードの簡易版を使用しています。

数値をMapオブジェクトに置き換えて描画する

Mapオブジェクトを使用すると、複数の属性を持つ1つの物事を表すことができます。属性は、今の場合でいうと、カードの色(白と黒)がこれに当たります。また8,4,2,1というポイントは、配列のインデックス位置で決まりますが、配列ができあがると要素の位置は変わらないので、配列の作成時にポイント属性としてMapオブジェクトに付加しておくこともできます。

下図は配列に入れた4つのMapオブジェクトを示しています。一番左のMapオブジェクトは、color属性を0にしているので、これで黒を表すことができます。またインデックス位置が0なので、point属性を8にしています。その右のMapオブジェクトも同様で、color属性を255に設定することで白を表すことができ、インデックス位置に合わせてpoint属性を4に設定します。以降も同様です。配列をシャッフルして要素のインデックス位置を変えない限り、このMapオブジェクトの配列は[黒、白、白、黒]を表すことができます。

ではこのコードを見てみましょう。次のコードでは、例やお題で出てきた矩形の並びを同時に描画しやすいように、Mapオブジェクトの配列を作成して返すsetCards()関数と、配列のポイントを計算するculcCards()関数、矩形を描画するdrawCards()関数を定義しています。複数の矩形の並びを同時に描画するので、変数yを設定し、矩形の並びを描画するたびに加算して矩形のy位置をずらしています。

function setup() {
    createCanvas(400, 300);
    background(220);

    let y = 20;
    // [黒、白、白、黒] = 9 を作る
    let arr = setCards([0, 255, 255, 0]);
    let total = culcCards(arr);
    print(total);
    drawCards(arr, y);

    // [黒、黒、黒、白]
    y += 50;
    arr = setCards([0, 0, 0, 255]);
    total = culcCards(arr);
    print(total);
    drawCards(arr, y);

    // [黒、白、黒、黒]
    y += 50;
    arr = setCards([0, 255, 0, 0]);
    total = culcCards(arr);
    print(total);
    drawCards(arr, y);

    // [黒、黒、白、黒]
    y += 50;
    arr = setCards([0, 0, 255, 0]);
    total = culcCards(arr);
    print(total);
    drawCards(arr, y);

    // [黒、白、黒、白]
    y += 50;
    arr = setCards([0, 255, 0, 255]);
    total = culcCards(arr);
    print(total);
    drawCards(arr, y);

}

// 白と黒を表す数値の配列を受け取り、Mapオブジェクトの配列を返す
// 受け取った配列を使って、Mapには白か黒の値と、インデックス位置に応じた数値(point)を設定する
function setCards(colArray) {
    let arr = [];
    for (let i = 0; i < 4; i++) {
        const m = new Map();
        m.set('color', colArray[i]);
        if (i === 0) {
            m.set('point', 8);
        }
        else if (i === 1) {
            m.set('point', 4);
        }
        else if (i === 2) {
            m.set('point', 2);
        }
        else if (i === 3) {
            m.set('point', 1);
        }
        arr.push(m);
    }
    return arr;
}

// Mapオブジェクトの配列を受け取り、黒の場合のみポイントを加算して返す
function culcCards(arr) {
    let total = 0;
    for (let i = 0; i < 4; i++) {
        const map = arr[i];
        const col = map.get('color');
        if (col === 0) {
            const p = arr[i].get('point');
            total += p;
        }
    }
    return total;
}

// Mapオブジェクトの配列と描画するy位置を受け取り、4つの矩形を横に描く
// Mapオブジェクトの色を調べ、それを塗り色にする
function drawCards(arr, y) {

    const rows = 4;
    const gutter = 5;
    const w = 30;
    const h = 30;
    const offsetX = 10;

    // 1 x 4 の矩形を描画
    for (let r = 0; r < rows; r++) {
        const col = arr[r].get('color');
        fill(col);
        rect(offsetX + r * (gutter + w), y, w, h);
    }
}

下図は実行結果です。

Mapオブジェクトはset()メソッドで属性が設定でき、get()メソッドで属性の値が取得できます。

コメントを残す

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

CAPTCHA