CODESAMPLE

Blackboard - C++

Share on:

The Blackboard pattern is a computational problem-solving technique where multiple independent knowledge sources (KSs) contribute to solving a problem via a shared data structure, the Blackboard. KSs ‘watch’ the Blackboard for changes that match their expertise, and when a match occurs, execute to modify the data accordingly. This allows for flexible, collaborative problem-solving without tight coupling between the KSs. The code demonstrates this with a simplified example of image processing: edge detection and shape analysis operate on pixel data in a shared Blackboard structure. C++’s flexibility allows representing KSs as classes with dedicated functions watching for conditions in the Blackboard.

 #include <iostream>
 #include <vector>
 #include <algorithm>
 #include <mutex>

 // The Blackboard - shared data
 class Blackboard {
 public:
  std::vector<std::vector<int>> data;
  std::mutex mutex;

  Blackboard(int rows, int cols) : data(rows, std::vector<int>(cols, 0)) {}

  void update_data(int row, int col, int value) {
    std::lock_guard<std::mutex> lock(mutex);
    data[row][col] = value;
  }

  int get_data(int row, int col) const {
    std::lock_guard<std::mutex> lock(mutex);
    return data[row][col];
  }
};

 // Knowledge Source Interface
 class KnowledgeSource {
 public:
  virtual void execute(Blackboard& blackboard) = 0;
  virtual bool should_execute(Blackboard& blackboard) = 0;
 };

 // Knowledge Source: Edge Detection
 class EdgeDetector : public KnowledgeSource {
 public:
  void execute(Blackboard& blackboard) override {
    for (size_t i = 1; i < blackboard.data.size() - 1; ++i) {
      for (size_t j = 1; j < blackboard.data[0].size() - 1; ++j) {
        // Simple edge detection (Sobel-like)
        int gx = blackboard.get_data(i - 1, j) - blackboard.get_data(i + 1, j);
        int gy = blackboard.get_data(i, j - 1) - blackboard.get_data(i, j + 1);
        int gradient_magnitude = std::abs(gx) + std::abs(gy);

        if (gradient_magnitude > 10) {
          blackboard.update_data(i, j, 255); // Mark edge
        }
      }
    }
  }

  bool should_execute(Blackboard& blackboard) override {
    // Execute if no edges have been detected yet
    for (auto& row : blackboard.data) {
      if (std::any_of(row.begin(), row.end(), [](int pixel){ return pixel == 255; })) {
        return false;
      }
    }
    return true;
  }
 };

 // Knowledge Source: Shape Analysis (Placeholder)
 class ShapeAnalyzer : public KnowledgeSource {
 public:
  void execute(Blackboard& blackboard) override {
    // In a real implementation, this would analyze shapes based on edge data.
    std::cout << "Shape analysis running..." << std::endl;
  }

  bool should_execute(Blackboard& blackboard) override {
    // Execute if edges are present
    for (auto& row : blackboard.data) {
      if (std::any_of(row.begin(), row.end(), [](int pixel){ return pixel == 255; })) {
        return true;
      }
    }
    return false;
  }
 };

 int main() {
  Blackboard blackboard(5, 5);

  // Initialize blackboard with some data
  blackboard.update_data(1, 1, 100);
  blackboard.update_data(1, 2, 150);
  blackboard.update_data(2, 1, 120);
  blackboard.update_data(2, 2, 200);

  // Create knowledge sources
  EdgeDetector edge_detector;
  ShapeAnalyzer shape_analyzer;

  // Execution loop
  std::vector<KnowledgeSource*> knowledge_sources = {&edge_detector, &shape_analyzer};
  for (auto& ks : knowledge_sources) {
    if (ks->should_execute(blackboard)) {
      ks->execute(blackboard);
    }
  }

  // Print the blackboard data
  for (const auto& row : blackboard.data) {
    for (int pixel : row) {
      std::cout << pixel << " ";
    }
    std::cout << std::endl;
  }

  return 0;
 }