kairo-gokko (23) リレー 2



前回は not リレーの表示まで実装しました。 今回は、リレーの通電状態が変わったときに隣のスイッチを切り替える部分を作りましょう。

汎用的なものを目指すのはいったん脇に置いといて、まずはこの回路を動かすことだけを考えて様子を探ります。最初はハリボテでOK。

f:id:sonota88:20200313094423p:plain


今、通電やスイッチなどの状態更新まわりの処理の流れはこうなっています。

- スイッチの状態更新
- スイッチの状態が変化したら以下を実行
  - 通電状態を更新
  - リレーの状態を更新
  - ランプの状態を更新

リレーの状態が変わった場合は隣のスイッチの状態も更新する必要があるので……こうでしょうか。

- スイッチの状態更新
- スイッチの状態が変化したら以下を実行
  - 通電状態を更新
  - リレーの状態を更新
    - このとき隣のスイッチの状態を更新 ★これを追加
  - ランプの状態を更新

スイッチの状態が変わったらまた通電判定をやりなおして、通電状態を更新しなければいけません。 そこで、通電状態の更新以下を繰り返してみます。

- スイッチの状態更新
- スイッチの状態が変わったら
  - 通電状態を更新
  - リレーの状態を更新
    - このとき隣のスイッチの状態を更新
  - ランプの状態を更新

  - 通電状態を更新
  - リレーの状態を更新
    - このとき隣のスイッチの状態を更新
  - ランプの状態を更新

これでどうでしょうか。やってみましょう。


えーっとまずは……リレーが作用する相手のスイッチを見つける必要がありますね。そこを作りましょう。

リレーは左の子回路にあり、相手のスイッチは右の回路にあります。 ということは、相手のスイッチを探す処理は子回路内に閉じた処理にはならないですね。

ではどうするかというと……ひとまず circuit にお願いして隣のスイッチを探してもらうことにします。

# class Circuit

  def update_not_relays_state
    @child_circuits.each { |child_circuit|
      child_circuit.update_not_relays(self)
    }
  end


# class ChildCircuit

  def update_not_relays(circuit)
    @not_relays.each { |not_relay|
      edge = @edges.find { |edge| edge.include_pos?(not_relay.pos) }
      not_relay.update(edge.on?)

      neighnor_switch = circuit.find_neighbor_switch(not_relay.pos)
    }
  end

隣のスイッチを探す部分は次のようにしました。

※ これまた非効率で、実行時に動的に探す必要はなかったりします。が、まずは富豪的にやってしまいます。

# class Circuit

  def find_neighbor_switch(pos)
    @child_circuits.each { |child_circuit|
      switch = child_circuit.find_neighbor_switch(pos)
      return switch if switch
    }

    nil
  end

# module Unit
#   class Point

    def translate(dx, dy)
      Point.new(@x + dx, @y + dy)
    end

# class ChildCircuit

  def neighbor?(pos1, pos2)
    pos1 == pos2.translate( 0, -1) ||
    pos1 == pos2.translate( 1,  0) ||
    pos1 == pos2.translate( 0,  1) ||
    pos1 == pos2.translate(-1,  0)
  end

  def find_neighbor_switch(pos)
    @switches.find { |switch|
      neighbor?(switch.pos, pos)
    }
  end

相手のスイッチが見つかったら、 そのスイッチの状態をリレーの状態に応じて更新します。

今作っているのは not リレーなので、 リレーと逆になるように状態をセットします。 トグルではないのがポイントですね。

--- a/child_circuit.rb
+++ b/child_circuit.rb
@@ -225,6 +225,7 @@ class ChildCircuit
       not_relay.update(edge.on?)
 
       neighbor_switch = circuit.find_neighbor_switch(not_relay.pos)
+      neighbor_switch.update(! not_relay.on?)
     }
   end
 

あとは、上で書いたように 通電状態の変更以下を単純に繰り返してあげます。

--- a/main.rb
+++ b/main.rb
@@ -124,6 +124,10 @@ def main_loop(circuit, view)
     circuit.update_tuden_state()
     circuit.update_not_relays_state()
     circuit.update_lamps_state()
+
+    circuit.update_tuden_state()
+    circuit.update_not_relays_state()
+    circuit.update_lamps_state()
   end
 
   draw(view, circuit, mx, my)

初回の状態更新の部分も同様に繰り返し。

--- a/main.rb
+++ b/main.rb
@@ -140,6 +140,10 @@ circuit.update_tuden_state()
 circuit.update_not_relays_state()
 circuit.update_lamps_state()
 
+circuit.update_tuden_state()
+circuit.update_not_relays_state()
+circuit.update_lamps_state()
+
 view = View.new(PPC)
 
 Sound.register(:click, "click.wav")

f:id:sonota88:20200315085917g:plain

おおー、動いた。ハリボテとはいえ、 not リレーによるスイッチの切り替えができるようになりました!

さらっとやってしまいましたが、なんと、記念すべき、最初の NOT ゲート が動くようになりました!!   めでたい!   🎉🎉🎉🎉

これは not リレーがなければできなかったことです。   すごい!   not リレーえらい!!   (今さらのように盛り上げ)

いやしかし、このリレーの登場以降の部分は自分にとって未知の世界 (本で読んだりしてボンヤリ知ってはいるけど実際に動くものを自分で作ったことはなかった) だったので、わくわくしますね!


以下の iframe で実際に動かせます。

こちらも同じものです。
https://sonota88.github.io/kairo-gokko/pages/23/index.html