class Rule
attr_reader :rule_name, :lhs_list, :rhs
def initialize(rule_name, lhs_list, rhs_str)
@rule_name = rule_name
@lhs_list = lhs_list
@rhs = rhs_str
end
end
class RuleFactory
@@rule_num = 0
def RuleFactory.make_rule(lhs_list, rhs)
@@rule_num += 1
Rule.new("R#{@@rule_num}", lhs_list, rhs)
end
def RuleFactory.clear_number
@@rule_num = 0
end
end
class WorkingMemory
def initialize
@knowledge_list = []
end
def find_knowledge(find_str)
return nil if nil == @knowledge_list
@knowledge_list.find { |know| know == find_str }
end
def add_knowledge(knowledge)
return if find_knowledge(knowledge)
@knowledge_list << knowledge
end
def load_from_file(file_path)
File.open(file_path, 'r') do |file|
file.each_line do |line|
line[-1] = '' if line[-1] == 10 # '\n' is 10(ascii)
add_knowledge(line)
end
end
end
def clear
@knowledge_list.clear
end
end
class ProductionMemory
def initialize
@rule_list = []
end
def add_rule(rule)
@rule_list << rule
end
# 등록된 순서대로 리턴
def find_rules(rhs_str)
# find rule RHS
@rule_list.select { |rule| rule.rhs == rhs_str }
end
def load_from_file(file_path)
RuleFactory.clear_number
File.open(file_path, 'r') do |file|
file.each_line do |line|
line[-1] = '' if line[-1] == 10 # '\n' is 10(ascii)
# split |
split_lhsrhs = line.split('|')
# split ,
split_each_lhs = split_lhsrhs[0].split(',')
add_rule(RuleFactory.make_rule(split_each_lhs, split_lhsrhs[1]))
end
end
end
def clear
@rule_list.clear
end
end
class RuleInterpreter
def initialize
@wm = nil
@pm = nil
@prev_fired_rule = nil
@unsatisfied_chain_stack =[]
end
def set_working_memory(wm)
@wm = wm
end
def set_production_memory(pm)
@pm = pm
end
def set_goal(goal_str)
@goal = goal_str
end
def fire_rule(rule)
# rhs 의 정보를 WM 으로 넣음
@wm.add_knowledge(rule.rhs)
puts "Fired #{rule.rule_name}"
@prev_fired_rule = rule
end
def do_bwd_recursive(target_knowledge)
# 찾아야 할 지식이 rhs 로 있는 rule 후보들을 찾음
next_candidate_rules = @pm.find_rules(target_knowledge)
while( next_candidate_rules.length > 0 )
# 남은 후보 중에서 하나를 선택
selected_rule = select_rule_with_conflict(next_candidate_rules)
# 이전에 fire 한 rule 만 남음
break if nil == selected_rule
# WM 에 있는 것들은 통과, 없는 것들에 대해 리스트 뽑기
unsatisfied_lhs_list = selected_rule.lhs_list.select do |lhs|
nil == @wm.find_knowledge(lhs)
end
if unsatisfied_lhs_list
# 만족하지 못하는 lhs 들을 찾아서 들어감(DFS)
unsatisfied_lhs_list.each do |lhs|
break if !do_bwd_recursive(lhs)
unsatisfied_lhs_list.delete(lhs)
end
end
# 만족하지 못하는 lhs 를 어떤 rule 로도 만들 수 없음
if unsatisfied_lhs_list && unsatisfied_lhs_list.length > 0
next_candidate_rules.delete(selected_rule)
puts "Abandoned #{selected_rule.rule_name}"
next
end
# WM 에 전부 있으면 여기까지 진입가능
# rule 을 fire
fire_rule(selected_rule)
# target_knowledge 는 해결함, 이전 미해결로 리턴
return true
end
# 후보 중 진행 가능한 것이 없음
return nil
end
def do_bwd
return nil if nil == @wm || nil == @pm
do_bwd_recursive(@goal)
end
def select_rule_with_conflict(rule_list)
# 3) specificity 적용, 2) Recency 는 적용하지 않음(WM의 순서를 뒤져야 함)
most_preferred_rule = nil
max_condition = 0
rule_list.each do |rule|
# 1) refraction 을 방지
next if @prev_fired_rule == rule
if nil == most_preferred_rule
most_preferred_rule = rule
max_condition = rule.lhs_list.length
else
if max_condition < rule.lhs_list.length
most_preferred_rule = rule
max_condition = rule.lhs_list.length
end
end
end
return most_preferred_rule if most_preferred_rule
# 이전 rule 밖에 남지 않음, 다시 fire 하지 않음
return nil
end
end
class ProductionSystem
def initialize
@wm = nil
@pm = nil
end
def set_working_memory(wm)
@wm = wm
end
def set_production_memory(pm)
@pm = pm
end
def do_bwd(goal)
interpreter = RuleInterpreter.new
interpreter.set_working_memory @wm
interpreter.set_production_memory @pm
interpreter.set_goal goal
found = interpreter.do_bwd
if found
puts "Success"
else
puts "Not Success"
end
end
def clear_knowledge
@wm = nil
end
def clear_rule
@pm = nil
end
end
# 데이터 넣기
ps = ProductionSystem.new
pm = ProductionMemory.new
pm.load_from_file 'zoo_production_rule.dat'
ps.set_production_memory pm
# GIRAFFE
puts 'Test:: Find giraffe'
wm = WorkingMemory.new
wm.load_from_file 'working_memory_ex_giraffe.dat'
ps.set_working_memory wm
ps.do_bwd('giraffe')
ps.clear_knowledge
# CHEETAH
puts 'Test:: Find cheetah'
wm = WorkingMemory.new
wm.load_from_file 'working_memory_ex_cheetah.dat'
ps.set_working_memory wm
ps.do_bwd('cheetah')