aboutsummaryrefslogtreecommitdiff
path: root/03-switch3.rb
blob: 7cbdc2f01f49372f495b226ca821e293f8b34f57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
require './level'
require './frame2'

$levels << Level.new do
  @interfaces = %w(1 2 3)
  @description = <<~EOD
    You are now a three-port switch.
    When a frame comes in on a port, you should keep track of the source address,
      and any frames destined for that address in future clicks should only be
      forwarded to that port. All other frames should be broadcast as before.
  EOD
  @clicks = 8

  @fib = @interfaces.product([nil]).to_h
  @next_fib = @fib.dup

  def target(frame)
    ifib = @fib.invert
    if ifib.keys.include?(frame.dst_addr)
      [frame.to(ifib[frame.dst_addr])]
    else
      case frame.iface
      when ?1
        [frame.to(?2), frame.to(?3)]
      when ?2
        [frame.to(?1), frame.to(?3)]
      when ?3
        [frame.to(?1), frame.to(?2)]
      else
        []
      end
    end
  end

  def generate
    frames = {}
    @interfaces.each do |iface|
      next if rand < 0.3

      if rand < 0.4 || @fib[iface].nil?
        src = Frame2.gen_addr
        @next_fib[iface] = src
      else
        src = @fib[iface]
      end

      dst = nil
      dst = (@fib.values.compact - [src]).sample if rand < 0.5
      dst = Frame2.gen_addr if dst.nil?

      frames[@count.to_s] = Frame2.new(iface, src, dst)
      @count += 1
    end
    frames
  end

  def click
    @fib = @next_fib
    @next_fib = @fib.dup
  end
end