0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

前回(Part 1)はこちら:
https://qiita.com/qtenky/items/a9923a434456f2194704

今回は、「人が乗りたいと言ったとき(外ボタンが押されたとき)」はどのイベントで拾えるのか? を起点に、Challenge #2: Transport 20 people in 60 seconds or less を解くための考え方と実装に進みます。


今回やること

前回は idle(エレベーターが暇になった瞬間)のイベントを扱いました。

じゃあ逆に、外で人が待っていて「上/下」ボタンが押された瞬間はどのイベント?

答えはこれです。

  • フロア側:up_button_pressed / down_button_pressed
  • エレベーター側:floor_button_pressed(乗った人が行き先を押す)

まずは外ボタン(フロア側)から拾っていきます。


elevators と floors は “配列” で渡ってくる

init の引数を見てください。

init: function(elevators, floors) {

ここにある通り、elevatorsfloors配列 です。

ドキュメントにもこうあります:

Do stuff with the elevators and floors, which are both arrays of objects

つまり中身のイメージはこう。

elevators = [elevator, elevator, elevator, ...];
floors    = [floor, floor, ...];

そしてサンプルではこうして1台目を取っています。

var elevator = elevators[0];

複数台あるとき「全員が同じ動き」だと事故る

ここでちょっと想像。

エレベーターが複数台あるのに、全員が同じロジックで同じ階へ向かったら…?

→ 無駄移動だらけで、効率が死にます。

解決策の一例としては、

  • エレベーターごとに担当範囲を分ける

    • 低層担当・高層担当みたいに分担する

例えばこんな感じ(今回はChallenge #2なので「考え方の紹介」だけ):

var mid = Math.floor(floors.length / 2);
  • Math.floor() は切り下げ(Math.floor(3.9) === 3
  • 四捨五入は Math.round()

2.5階みたいな謎フロアを作らないためにも、整数にするのは大事です。


ただし今回は Challenge #2(エレベーター1台)に戻る

今回は前提がシンプル。

  • エレベーター:1台

  • 目的:外で待っている人を素早く拾って運び切る

  • 制限:60秒

    • 無駄移動・重複処理は即アウト

つまり肝はこれ:

  1. 外ボタンが押された階を「呼び出し」として記録する
  2. 重複登録を防ぐ
  3. idle になったら、今ある呼び出しの中で 一番近い階へ行く(絶対値で距離計算)
  4. 行くと決めた呼び出しは配列から消す(←次回やる)

外ボタンを記録する requests を作る

呼び出し階を溜める配列 requests を用意します。

requests = [0, 1, 4];

これは「0階、1階、4階から呼ばれている」状態。

重複を防ぐには includes を使うのが定番です。

if (!requests.includes(floorNum)) {
    requests.push(floorNum);
}
  • includes:入ってたら true、なければ false
  • push:末尾に追加

注意:floors.on は書けない(floors は配列)

ここ、つまずきポイントです。

floors は配列なのでこうはできません:

floors.on("up_button_pressed", ...)

代わりに、各 floor に対してイベントを登録します。


外ボタンイベントを requests に追加する

{
  init: function(elevators, floors) {
    var elevator = elevators[0];
    var requests = [];

    floors.forEach(function(floor) {
      floor.on("up_button_pressed", function() {
        var floorNum = floor.floorNum();
        if (!requests.includes(floorNum)) {
          requests.push(floorNum);
        }
      });

      floor.on("down_button_pressed", function() {
        var floorNum = floor.floorNum();
        if (!requests.includes(floorNum)) {
          requests.push(floorNum);
        }
      });
    });
  },
  update: function(dt, elevators, floors) {}
}

「これだめ?」:floor.floorNum() 直書きでも動くけど、冗長になりやすい

たとえばこういう書き方:

floor.on("down_button_pressed", function() {
  if (!requests.includes(floor.floorNum())) {
    requests.push(floor.floorNum());
  }
});

これ自体が間違いではないです。動きます。

でも floor.floorNum() を2回呼んでいて読みづらいし、updown で同じ処理をコピペし始めると、すぐコードが太ります。

なので、一度変数に入れて使い回すのが読みやすいです。


次は「どの階に行くべきか」を絶対値で決める

idle(暇)になったら、requests の中から 今いる階に一番近い階へ向かいます。

距離は絶対値:

Math.abs(target - current)

ここでは

  • bestFloor(今選ぶべき階)
  • bestDist(その距離)

を使います。

var current = elevator.currentFloor();
var bestFloor = null;
var bestDist = Infinity;

for (var i = 0; i < requests.length; i++) {
  var dist = Math.abs(requests[i] - current);
  if (dist < bestDist) {
    bestDist = dist;
    bestFloor = requests[i];
  }
}

ここまで統合:idle で最寄りの呼び出しへ向かう

{
  init: function(elevators, floors) {
    var elevator = elevators[0];
    var requests = [];

    floors.forEach(function(floor) {
      var register = function() {
        var floorNum = floor.floorNum();
        if (!requests.includes(floorNum)) {
          requests.push(floorNum);
        }
      };

      floor.on("up_button_pressed", register);
      floor.on("down_button_pressed", register);
    });

    elevator.on("idle", function() {
      if (requests.length === 0) return;

      var current = elevator.currentFloor();
      var bestFloor = null;
      var bestDist = Infinity;

      for (var i = 0; i < requests.length; i++) {
        var dist = Math.abs(requests[i] - current);
        if (dist < bestDist) {
          bestDist = dist;
          bestFloor = requests[i];
        }
      }

      elevator.goToFloor(bestFloor);
    });
  },

  update: function(dt, elevators, floors) {}
}

次回予告:選んだ階は requests から消す

ここまでで、

  • 外ボタン押下 → requests に登録
  • idle → 最寄り階を選んで向かう

まではできました。

次はここです:

3) 選んだ階は requests から消す
行くと決めたら、それはもう「呼び出し」じゃないので削除する。

ここまでで Part 2 は一旦終了です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?