0611073adbcf46f93b7d8bf059c109b8

You will need Shoes installed to run this sketch: http://code.whytheluckystiff.net/shoes/

I'm looking for speed optimizations and a reduction in code-size (but not at the expense of legibility).

Also, coming from a C/Javascript background, I'd appreciate suggestions that change my code to better follow the 'Ruby way'.

Tested with Shoes 0.r396, Windows XP.

Thank you kindly.

# Boids - A Shoes Application
#
# Author       : Wally Glutton - http://stungeye.com
# Summary      : A hungry swarm indeed!
#
# Notes        : My home-rolled Vector class appears to be quicker than the matrix library Vectors.
# Boid Algo    : http://www.vergenet.net/~conrad/boids/pseudocode.html
#
# Required     : You must have Shoes installed to view the boids.
# Get Shoes    : http://code.whytheluckystiff.net/shoes/
# Learn Shoes  : http://hackety.org/press/nks.html
#
# Code License : http://creativecommons.org/licenses/by-sa/2.5/ca/


srand
NUM_BOIDS = 30
NUM_FOODSTUFF = 6
boids = []
foodstuff = []

Shoes.app do
    stroke rgb(0x30, 0x30, 0x05, 0.5)
    app = self
    NUM_BOIDS.times { |i| boids[i] = Boid.new rand * self.width, rand * self.height, random(-5, 5), random(-5, 5) }
    NUM_FOODSTUFF.times { |i| foodstuff[i] = Food.new app}
    animate(24) do
        clear do
            boids.each do |boid|
                boid.calculate_avoidance_delta boids   # Avoid other boids
                boid.calculate_attraction_delta boids  # Gravitate towards the centre-of-mass of nearby boids
                boid.calculate_allignment_delta boids  # Allign velocity with nearby boids
                boid.calculate_hunting_delta foodstuff # Be on the lookout for food
                boid.calculate_stay_visible_delta self # Don't fly too far from home
                
                boid.apply_deltas
                boid.limit_speed
                
                boid.move
                boid.draw self, app
                
                foodstuff.each do |food|
                    food.eaten? boid
                end
            end
            foodstuff.each do |food|
                food.draw self
            end
        end
    end
end

class Food
    RADIUS = 30
    attr_reader :position
    def initialize app
        @app = app
        spawn
    end
    def spawn
        @size = RADIUS
        @position = VectorK.new rand * @app.width, rand * @app.height
    end
    def eaten? boid
        if @position.nearby? RADIUS, boid.position
            @size -= 1
        end
        if @size < 0
            spawn
        end
    end
    def draw slot
        @app.fill rgb(0xFF, 0x30, 0xFF, 0.4)
        slot.oval :left => @position.x, :top => @position.y, :radius => @size, :center => true
    end
end

class Boid
    RADIUS = 20
    MAX_SPEED = 25
    AVOID_RADIUS = RADIUS*3 # Avoid other boids within this radius
    AVOID_DAMPER = 100
    ATTRACTION_RADIUS = RADIUS*8 # Gravitate to the centre of mass of boids within this radius
    ATTRACTION_DAMPER = 30
    ALLIGNMENT_RADIUS = RADIUS*3 # Allign velocity with boids within this radius
    ALLIGNMENT_DAMPER =  30
    HUNTING_RADIUS = RADIUS*5 # Locate food within this radius
    HUNTUNG_DAMPER = 10
    STAY_VISIBLE_DAMPER = 300
    
    attr_reader :velocity, :position
    
    def initialize x, y, vx, vy
        @velocity = VectorK.new vx, vy 
        @position = VectorK.new x, y
        @velocity_delta = VectorK.new 0, 0
    end
    def calculate_avoidance_delta boids
        boids.each do |other|
            if @position.nearby? AVOID_RADIUS, other.position
                @velocity_delta += (@position - other.position) / AVOID_DAMPER
            end
        end
    end
    def calculate_attraction_delta boids
        average_position = VectorK.new 0, 0
        visible_boids = 0
        boids.each do |other|
            if @position.nearby? ATTRACTION_RADIUS, other.position
                average_position += other.position
                visible_boids += 1
            end
        end
        average_position /= visible_boids
        @velocity_delta +=  (average_position - @position) / ATTRACTION_DAMPER
    end
    def calculate_allignment_delta boids
        allignment_delta = VectorK.new 0, 0
        visible_boids = 0
        boids.each do |other|
            if @position.nearby? ALLIGNMENT_RADIUS, other.position
                allignment_delta += other.velocity
                visible_boids += 1
            end
        end
        allignment_delta /= visible_boids
        @velocity_delta += allignment_delta / ALLIGNMENT_DAMPER
    end
    def calculate_hunting_delta foodstuff
        foodstuff.each do |food|
            if @position.nearby? HUNTING_RADIUS, food.position
               @velocity_delta += (food.position - @position) / HUNTUNG_DAMPER
            end
        end
    end
    def calculate_stay_visible_delta slot
        mid_x = slot.width / 2
        mid_y = slot.height / 2
        @velocity_delta -= (@position - VectorK.new(mid_x, mid_y)) / STAY_VISIBLE_DAMPER
    end
    def apply_deltas
        @velocity += @velocity_delta
        @velocity_delta = VectorK.new 0, 0
    end
    def limit_speed
        if @velocity.r > MAX_SPEED
            @velocity /= @velocity.r # Create a unit vector
            @velocity *= MAX_SPEED   # Scale to max speed
        end
    end
    def move
        @position += @velocity
    end
    def draw slot, app
        app.fill rgb(0x30, 0xFF, 0xFF, 0.5)
        slot.oval :left => @position.x, :top => @position.y, :radius => RADIUS, :center => true
        slot.line @position.x, @position.y, (@position.x + @velocity.x), (@position.y + @velocity.y)
    end
end

class VectorK
    attr_reader :x, :y
    def initialize x, y
        @x = x
        @y = y
    end
    def nearby? threshold, a
        return false if a === self
        (distance a) < threshold
    end
    def distance a
        Math.sqrt((@x - a.x)**2 + (@y - a.y)**2)
    end
    def / a
        if (a != 0)
            VectorK.new(@x / a, @y / a)
        else
            self
        end
    end
    def + a
        VectorK.new(@x + a.x, @y + a.y)
    end
    def - a
        VectorK.new(@x - a.x, @y - a.y)
    end
    def * a
        VectorK.new(@x * a, @y * a)
    end
    def r
        Math.sqrt(@x * @x + @y * @y)
    end
end

def random min, max
    choice = (max - min > 0) ? (rand max - min) + min : 0;
end

Refactorings

No refactoring yet !

Your refactoring





Format Copy from initial code

or Cancel