CODESAMPLE

Blackboard - Ruby

Share on:

The Blackboard pattern is a computational architecture for solving problems that can’t be solved with a deterministic algorithm. It consists of several knowledge sources (independent modules) that contribute to solving a problem by observing a shared data structure – the “blackboard”. These sources react to changes on the blackboard, adding or modifying information.

This Ruby implementation simulates a simple Blackboard for recognizing patterns in data. Blackboard holds the current data. KnowledgeSource classes define rules to modify the blackboard. DataProcessor adds initial data and triggers the process. The process method in DataProcessor iterates, applying knowledge sources until a solution is found or a maximum iteration count is reached. This approach is idiomatic Ruby due to its emphasis on modularity and using objects to encapsulate behavior. The use of a hash for the blackboard aligns with Ruby’s flexible data structures.

# blackboard.rb

class Blackboard
  attr_accessor :data

  def initialize
    @data = {}
  end
end

class KnowledgeSource
  def initialize(blackboard)
    @blackboard = blackboard
  end

  def apply
    raise NotImplementedError, "Subclasses must implement the 'apply' method"
  end
end

class PatternRecognizer < KnowledgeSource
  def initialize(blackboard)
    super
  end

  def apply
    if @blackboard.data[:numbers] && @blackboard.data[:numbers].is_a?(Array)
      if @blackboard.data[:numbers].include?(1) && @blackboard.data[:numbers].include?(2) && @blackboard.data[:numbers].include?(3)
        @blackboard.data[:pattern_found] = "Sequence 1, 2, 3 detected!"
        return true # Signal solution found
      end
    end
    false
  end
end

class NumberExtractor < KnowledgeSource
  def initialize(blackboard)
    super
  end

  def apply
    input_string = @blackboard.data[:input]
    if input_string
      numbers = input_string.scan(/\d+/).map(&:to_i)
      @blackboard.data[:numbers] = numbers
      return true
    end
    false
  end
end

class DataProcessor
  def initialize(blackboard)
    @blackboard = blackboard
    @knowledge_sources = []
  end

  def add_knowledge_source(source)
    @knowledge_sources << source
  end

  def process(input_data, max_iterations = 10)
    @blackboard.data[:input] = input_data
    iteration = 0

    while iteration < max_iterations
      changed = false
      @knowledge_sources.each do |source|
        if source.apply
          changed = true
        end
      end
      break if !changed

      iteration += 1
    end

    @blackboard.data
  end
end

# Example Usage
blackboard = Blackboard.new
processor = DataProcessor.new(blackboard)
processor.add_knowledge_source(NumberExtractor.new(blackboard))
processor.add_knowledge_source(PatternRecognizer.new(blackboard))

result = processor.process("This string contains the numbers 1, 2, and 3.")
puts result # Output: {:input=>"This string contains the numbers 1, 2, and 3.", :numbers=>[1, 2, 3], :pattern_found=>"Sequence 1, 2, 3 detected!"}

result2 = processor.process("The numbers are 4, 5, 6")
puts result2 # Output: {:input=>"The numbers are 4, 5, 6", :numbers=>[4, 5, 6]}