RulesEngine

class
Superclass
Object
Extended With
Loggability

An engine for iterating over successive applications of the L-System's ruleset to its axiom.

Public Class Methods

anchor
new( &block )

Create a new rules engine for an L-System. If the block is present, it is called with the new instance as self.

# File lib/l_system/rules_engine.rb, line 21
def initialize( &block )
        @variables = Set.new
        @constants = Set.new
        @axiom = nil
        @rules = []

        @rules_as_hash = nil

        self.instance_eval( &block ) if block
end

Public Instance Methods

anchor
alphabet()

Return the system's variables and constants.

# File lib/l_system/rules_engine.rb, line 84
def alphabet
        return @variables | @constants
end
anchor
apply_rules( state )

Apply the system's rules to the given state and return the result.

# File lib/l_system/rules_engine.rb, line 121
def apply_rules( state )
        rules_hash = self.rules_as_hash
        return state.each_char.with_object( String.new(encoding: 'utf-8') ) do |char, new_state|
                new_state << rules_hash[ char ]
        end
end
anchor
axiom( new_value=nil )

Get/set the axiom of the system.

# File lib/l_system/rules_engine.rb, line 90
def axiom( new_value=nil )
        self.axiom = new_value if new_value
        return @axiom
end
anchor
axiom=( new_value )

Set the axiom of the system.

# File lib/l_system/rules_engine.rb, line 97
def axiom=( new_value )
        @axiom = new_value
end
anchor
constants( *new_values )

Get/set the system's constants (the static parts of its alphabet).

# File lib/l_system/rules_engine.rb, line 63
def constants( *new_values )
        self.constants = new_values unless new_values.empty?
        return @constants.dup
end
anchor
constants=( new_values )

Set the systems constants to new_values.

# File lib/l_system/rules_engine.rb, line 70
def constants=( new_values )
        @rules_as_hash = nil

        new_values = Set.new( new_values, &:to_s )
        unless new_values.disjoint?( self.variables )
                common_char = (new_values & self.variables).to_a.first
                raise ArgumentError, "%p is already included in the variable set" % [ common_char ]
        end

        @constants = new_values
end
anchor
each( &block )

Yield each successive generation to the given block, or return an Enumerator that will do so if no block is given.

# File lib/l_system/rules_engine.rb, line 131
def each( &block )
        iter = Enumerator.new do |yielder|
                state = new_state = self.axiom.dup

                begin
                        yielder.yield( new_state )
                        state = new_state
                        new_state = self.apply_rules( state )
                end until state == new_state
        end

        return iter unless block
        return iter.each( &block )
end
anchor
rules( *new_values )

Get/set the system's rules.

# File lib/l_system/rules_engine.rb, line 103
def rules( *new_values )
        self.rules = new_values unless new_values.empty?
        return @rules
end
anchor
rules=( new_values )

Set the system's rules.

# File lib/l_system/rules_engine.rb, line 110
def rules=( new_values )
        @rules_as_hash = nil
        @rules = Array( new_values ).map( &:to_s )
end
anchor
variables( *new_values )

Get/set the system's variables (the replaceable parts of its alphabet).

# File lib/l_system/rules_engine.rb, line 42
def variables( *new_values )
        self.variables = new_values unless new_values.empty?
        return @variables.dup
end
anchor
variables=( new_values )

Set the systems variables to new_values.

# File lib/l_system/rules_engine.rb, line 49
def variables=( new_values )
        @rules_as_hash = nil

        new_values = Set.new( new_values, &:to_s )
        unless new_values.disjoint?( self.constants )
                common_char = (new_values & self.constants).to_a.first
                raise ArgumentError, "%p is already included in the constant set" % [ common_char ]
        end

        @variables = new_values
end

Protected Instance Methods

anchor
parse_rule( rule )

Return a tuple of the predecessor and successor of the given rule.

# File lib/l_system/rules_engine.rb, line 170
def parse_rule( rule )
        predecessor, successor = rule.strip.split( /\s*(?:->|→)\s*/, 2 )
        self.log.debug "Parsed rule: %p -> %p" % [ predecessor, successor ]
        successor_set = Set.new( successor.chars )

        raise "Invalid rule: predecessor %p is not in the variable set %p" %
                [ predecessor, self.variables ] unless self.variables.include?( predecessor )
        raise "Invalid rule: successor %p contains characters not in the alphabet %p" %
                [ successor, self.alphabet ] unless self.alphabet.superset?( successor_set )

        return predecessor, successor
end
anchor
rules_as_hash()

Return a Hash of tranforms that should be applied to a state during a generation.

# File lib/l_system/rules_engine.rb, line 153
def rules_as_hash
        unless @rules_as_hash
                @rules_as_hash = self.rules.each_with_object( {} ) do |rule, hash|
                        pred, succ = self.parse_rule( rule )
                        hash[ pred ] = succ
                end

                self.alphabet.each do |char|
                        @rules_as_hash[ char ] = char unless @rules_as_hash.key?( char )
                end
        end

        return @rules_as_hash
end