Langton's ant: Difference between revisions

Content added Content deleted
Line 3,252: Line 3,252:
=={{header|Ruby}}==
=={{header|Ruby}}==
<lang ruby>class Ant
<lang ruby>class Ant
Directions = [:north, :east, :south, :west]
class OutOfBoundsException < StandardError; end

def initialize(plane, pos_x, pos_y)
class Plane
@plane = plane
def initialize(x, y)
@position = Position.new(plane, pos_x, pos_y)
@size_x, @size_y = x, y
@cells = Array.new(y) {Array.new(x, :white)}
end
def white?(px, py)
@cells[py][px] == :white
end
def toggle_colour(px, py)
@cells[py][px] = (white?(px, py) ? :black : :white)
end
def check_bounds(px, py)
unless (0 <= px and px < @size_x) and (0 <= py and py < @size_y)
raise OutOfBoundsException, "(#@size_x, #@size_y)"
end
end
def to_s
@cells.collect {|row|
row.collect {|cell| cell == :white ? "." : "#"}.join + "\n"
}.join
end
end
dir_move = [[:north, [0,-1]], [:east, [1,0]], [:south, [0,1]], [:west, [-1,0]]]
Move = Hash[dir_move]
directions = dir_move.map{|dir, move| dir} # [:north, :east, :south, :west]
Right = Hash[ directions.zip(directions.rotate).to_a ]
Left = Right.invert
def initialize(size_x, size_y, pos_x=size_x/2, pos_y=size_y/2)
@plane = Plane.new(size_x, size_y)
@pos_x, @pos_y = pos_x, pos_y
@direction = :south
@direction = :south
@plane.check_bounds(@pos_x, @pos_y)
end
end
attr_reader :plane, :direction, :position

def run
def run
moves = 0
moves = 0
loop do
loop do
begin
begin
if $DEBUG and moves % 100 == 0
system "clear"
puts "%5d %s" % [moves, position]
puts plane
end
moves += 1
moves += 1
move
move
Line 3,278: Line 3,307:
moves
moves
end
end

def move
def move
plane.at(position).toggle_colour
@plane.toggle_colour(@pos_x, @pos_y)
position.advance(direction)
advance
if plane.at(position).white?
if @plane.white?(@pos_x, @pos_y)
@direction = Right[@direction]
turn(:right)
else
else
@direction = Left[@direction]
turn(:left)
end
end
end
end

def turn(left_or_right)
def advance
idx = Directions.index(direction)
dx, dy = Move[@direction]
case left_or_right
@pos_x += dx
@pos_y += dy
when :left then @direction = Directions[(idx - 1) % Directions.length]
@plane.check_bounds(@pos_x, @pos_y)
when :right then @direction = Directions[(idx + 1) % Directions.length]
end
end
end
end
def position

"(#@pos_x, #@pos_y)"
class Plane
def initialize(x, y)
@x = x
@y = y
@cells = Array.new(y) {Array.new(x) {Cell.new}}
end
end
attr_reader :x, :y

def at(position)
@cells[position.y][position.x]
end

def to_s
def to_s
@cells.collect {|row|
@plane.to_s
row.collect {|cell| cell.white? ? "." : "#"}.join + "\n"
}.join
end
end
end
end

class Cell
def initialize
@colour = :white
end
attr_reader :colour

def white?
colour == :white
end

def toggle_colour
@colour = (white? ? :black : :white)
end
end

class Position
def initialize(plane, x, y)
@plane = plane
@x = x
@y = y
check_bounds
end
attr_accessor :x, :y

def advance(direction)
case direction
when :north then @y -= 1
when :east then @x += 1
when :south then @y += 1
when :west then @x -= 1
end
check_bounds
end

def check_bounds
unless (0 <= @x and @x < @plane.x) and (0 <= @y and @y < @plane.y)
raise OutOfBoundsException, to_s
end
end

def to_s
"(%d, %d)" % [x, y]
end
end

class OutOfBoundsException < StandardError; end


#
#
# the simulation
# the simulation
#
#
ant = Ant.new(Plane.new(100, 100), 50, 50)
ant = Ant.new(100, 100)
moves = ant.run
moves = ant.run
puts "out of bounds after #{moves} moves: #{ant.position}"
puts "out of bounds after #{moves} moves: #{ant.position}"
puts ant.plane</lang>
puts ant</lang>


{{out}}
output
<pre style="height: 40ex; overflow: scroll">out of bounds after 11669 moves: (26, -1)
<pre style="height: 40ex; overflow: scroll">out of bounds after 11669 moves: (26, -1)
..........................#.#.......................................................................
..........................#.#.......................................................................