この記事の詳しい内容には毎日新聞の「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()メソッドで属性の値が取得できます。