前回はエッジ1本と複数のスイッチのケースまで対応しました。
今回はもう少し複雑なパターンとして、途中で分岐してスイッチが並列になっている回路の通電判定を考えます。
「並列つなぎ」というやつですね。 小学校の理科でやるレベルのものですから、さすがにこのくらいはなんとかしたい。
少し整理して、エッジとスイッチに e1
〜e4
, s1
, s2
と名前を付けました。
まずは「このパターンになっているか?」を調べて、 次に通電判定を行う、という方針で進めましょう。
(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」と単純に判断してしまうと、たとえば
こんな回路も 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本のエッジ(
e2
とe3
)に着目する e2
とe3
のそれぞれについて、- エッジ上のスイッチがすべて ON になっていたら「通電している」と判定する
e2
とe3
のいずれかが通電していれば、 プラス極・マイナス極に直接つながっている 2本のエッジ(e1
とe4
)も「通電している」と判定する
この手順でなんとかなりそうな気がします。
参考までに図を再掲。
真ん中の 2本のエッジ(e2
と e3
)はこれで取り出せます。
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 }
さて、取り出せたら次は e2
と e3
それぞれについて通電判定を行いますが、
ここでエッジとスイッチの対応関係について考慮する必要があります。
たとえば 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) }
そして、e2
、 e3
の通電状態をもとに
e1
、 e2
の通電状態を決めて更新。
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個にした場合の動作例を貼っておきます。
( https://sonota88.github.io/kairo-gokko/pages/14/index.html で試せます。※マウス操作のできないスマホ・タブレットは非対応。 )
実装を知らない人が表面的な動作だけ見たら 「へー、ちゃんと動いてるじゃん」 と言ってくれるんじゃないでしょうか?