kairo-gokko (14) 通電 4



前回はエッジ1本と複数のスイッチのケースまで対応しました。

今回はもう少し複雑なパターンとして、途中で分岐してスイッチが並列になっている回路の通電判定を考えます。

f:id:sonota88:20200301041056p:plain

「並列つなぎ」というやつですね。 小学校の理科でやるレベルのものですから、さすがにこのくらいはなんとかしたい。


少し整理して、エッジとスイッチに e1e4, s1, s2 と名前を付けました。

f:id:sonota88:20200301052456p:plain

まずは「このパターンになっているか?」を調べて、 次に通電判定を行う、という方針で進めましょう。

(1) パターンの認識

前回とはエッジの本数が違いますので、まずはそれで分岐させましょうか。

class Circuit
  # ...

  def update_4_edges
    # これから書く
  end

  def update_edges
    case @edges.size
    when 1
      is_tuden = Tuden.tuden?(@switches)
      @edges[0].update(is_tuden)
    when 4
      update_4_edges()
    else
      raise "not yet implemented"
    end
  end

  # ...

エッジ 1本の場合は前回と同じロジックをそのまま使います。 4本の場合はこれから作ります。 それ以外の場合は、未対応ということにしてとりあえず例外を投げておきます。

( 一応書いておくと、「こういう場当たり的なやり方はいまいちだ」 ということは承知の上であえてやっている……という体なので、 今回のはあまり真面目に読まなくても大丈夫です。 )


「エッジが4本ならOK」と単純に判断してしまうと、たとえば

f:id:sonota88:20200301055428p:plain

こんな回路も OK になってしまいます。 こういうのはお断りしたいので、えーと……とりあえず、 「電池のプラス極、マイナス極につながっているエッジがそれぞれ 1本ずつ であれば OK」ということにしましょうか。

これだけでは全然厳密ではなくて、はっきり言ってかなりザルです。 が、 細かくチェックしようとするときりがない(たとえば、 e1, e4 上にスイッチがあるかとか……)のでいったん簡易に済ませます。


エッジ(の両端のうちいずれか)がプラス極・マイナス極につながっているかを判定するために Edge#connected_to?() を追加して、

  class Edge
    # ...

    def connected_to?(pos)
      @pos1 == pos || @pos2 == pos
    end
  end

1本ずつであることを判定。

  def update_4_edges
    edges_connected_to_plus =
      @edges.select { |edge|
        edge.connected_to?(@plus_poles[0].pos)
      }
    edges_connected_to_minus =
      @edges.select { |edge|
        edge.connected_to?(@minus_poles[0].pos)
      }

    if edges_connected_to_plus.size == 1 &&
       edges_connected_to_minus.size == 1
      # OK
    else
      raise "not yet implemented"
    end

    # TODO
  end

※ プラス極、マイナス極が複数ある場合も今は考えず、それぞれ 1個ずつ存在する前提で進めます……。

形の判定はひとまずこれでよしとして、通電判定に進みます。

(2) 通電の判定

  • 4本のエッジのうち、プラス極・マイナス極に直接つながっていない 真ん中の 2本のエッジ(e2e3)に着目する
  • e2e3 のそれぞれについて、
    • エッジ上のスイッチがすべて ON になっていたら「通電している」と判定する
  • e2e3 のいずれかが通電していれば、 プラス極・マイナス極に直接つながっている 2本のエッジ(e1e4)も「通電している」と判定する

この手順でなんとかなりそうな気がします。

参考までに図を再掲。

f:id:sonota88:20200301052456p:plain


真ん中の 2本のエッジ(e2e3)はこれで取り出せます。

    edge_connected_to_plus = edges_connected_to_plus[0]
    edge_connected_to_minus = edges_connected_to_minus[0]

    center_edges =
      @edges.reject { |edge|
        edge == edge_connected_to_plus ||
        edge == edge_connected_to_minus
      }

さて、取り出せたら次は e2e3 それぞれについて通電判定を行いますが、 ここでエッジとスイッチの対応関係について考慮する必要があります。

たとえば e2 について処理するときは、e2 上にあるスイッチ s1 だけを考えればよく、 s2 は無視してよいでしょう。

同様に、 e3 について処理するときは、e3 上にあるスイッチ s2 だけを考えればよく、 s1 は無視してよいでしょう。

要するに、処理対象にしている 1本のエッジ上にあるスイッチだけを考慮すればよいということです。

なので、あるエッジ上に載っているスイッチを探すところから始めます。


WireFragment#connected_to?()Edge#include_pos?() を追加。

module Unit
  class WireFragment
    def connected_to?(pos)
      @pos1 == pos || @pos2 == pos
    end
  end

  class Edge
    def include_pos?(pos)
      @wfs.any? { |wf| wf.connected_to?(pos) }
    end
  end

ちょっと非効率ですが先に進みます。


エッジ上にあるスイッチだけを集めて、 あとは前回と同様に Tuden.tuden?() に渡し、 エッジの通電状態を更新します。

    center_edges.each { |edge|
      switches = @switches.select { |switch|
        edge.include_pos?(switch.pos)
      }
      is_tuden = Tuden.tuden?(switches)
      edge.update(is_tuden)
    }

そして、e2e3 の通電状態をもとに e1e2 の通電状態を決めて更新。

    is_tuden_center =
      center_edges.any? { |edge| edge.on? }

    edge_connected_to_plus.update(is_tuden_center)
    edge_connected_to_minus.update(is_tuden_center)

いろいろ雑でゆるゆるなのですが、とにかく動くようにはなりました。 簡単のためスイッチ 2個で説明してきましたが、 せっかくなのでスイッチ 4個にした場合の動作例を貼っておきます。

f:id:sonota88:20200301072432g:plain

https://sonota88.github.io/kairo-gokko/pages/14/index.html で試せます。※マウス操作のできないスマホタブレットは非対応。 )

実装を知らない人が表面的な動作だけ見たら 「へー、ちゃんと動いてるじゃん」 と言ってくれるんじゃないでしょうか?