Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class Cell
- attr_reader :type
- attr_reader :value
- attr_reader :glpyh
- attr_accessor :position
- def initialize(type, value, glpyh, position)
- @type = type
- @value = value
- @glpyh = glpyh
- @position = position
- end
- def empty?
- return self.type == :empty
- end
- def self.empty(x, y)
- return Cell.new(:empty, nil, " ", [x, y])
- end
- def self.parse_tile(char, x, y)
- case char
- when "?"
- return Tile.new(:input, nil, char, [x, y])
- when "*"
- return Tile.new(:scanner, nil, char, [x, y])
- when "F"
- return Tile.new(:furnace, nil, char, [x, y])
- when ">"
- return Tile.new(:belt_right, nil, char, [x, y])
- when "<"
- return Tile.new(:belt_left, nil, char, [x, y])
- when "i"
- return Tile.new(:copy_up, nil, char, [x, y])
- when "!"
- return Tile.new(:copy_down, nil, char, [x, y])
- when "r"
- return Tile.new(:random, nil, char, [x, y])
- when "T"
- return Tile.new(:turning_point, nil, char, [x, y])
- when "["
- return Tile.new(:bulldozer_right, nil, char, [x, y])
- when "]"
- return Tile.new(:bulldozer_left, nil, char, [x, y])
- when "\\"
- return Tile.new(:ramp_right, nil, char, [x, y])
- when "/"
- return Tile.new(:ramp_left, nil, char, [x, y])
- when /\d/
- return Value.new(:data, char.to_i, char, [x, y])
- when " "
- return empty(x, y)
- else
- return Tile::WALL
- end
- end
- end
- class Tile < Cell
- WALL = Tile.new(:wall, nil, "=", [0, 0])
- end
- class Value < Cell
- end
- class VocalRunner
- def initialize(initial_world, world_name, args)
- @world = initial_world.strip.split("\n").map.with_index do |row, j|
- row.split("").map.with_index do |char, i|
- Cell.parse_tile(char, i, j)
- end
- end
- @name = world_name
- @input = [*args]
- @output = []
- @debug = []
- validate_world
- end
- def validate_world
- all_cells = @world.flatten
- if all_cells.count { |cell| cell.type == :input } > 1
- raise "InvalidWorldException: More than one input found."
- end
- end
- def add_value(x, y, value)
- if value.is_a?(Numeric)
- if (0..9).include?(value)
- glpyh = value.to_s
- else
- glpyh = "#"
- end
- elsif value.is_a?(String)
- glpyh = "\""
- else
- glpyh = "#"
- end
- cell = Value.new(:data, value, glpyh, [x, y])
- set_cell(x, y, cell)
- end
- def get_cell(x, y)
- return Tile::WALL if y < 0 or y >= @world.size
- return Tile::WALL if x < 0 or x >= @world[y].size
- return @world[y][x]
- end
- def set_cell(x, y, cell)
- return if y < 0 or y >= @world.size
- return if x < 0 or x >= @world[y].size
- @world[y][x] = cell
- cell.position = [x, y]
- end
- def clear_cell(x, y)
- set_cell(x, y, Cell.empty(x, y))
- end
- def move_cell_horizontal(x, y, dx)
- value = get_cell(x, y)
- target = [x + dx, y]
- if get_cell(*target).empty?
- set_cell(*target, value)
- clear_cell(x, y)
- return true
- # elsif ramp
- # TODO: handle ramps
- end
- return false
- end
- def get_input
- return @input.shift
- end
- def add_output(value)
- @output.push(value)
- end
- def log(message)
- @debug.push(message)
- end
- def run
- display
- while tick
- begin
- puts
- display
- sleep(0.1)
- rescue Exception
- break
- end
- end
- unless @output.empty?
- puts
- puts @output.join("\n")
- end
- unless @debug.empty?
- puts
- puts @debug.join("\n")
- end
- end
- def display
- @world.each do |row|
- puts row.map { |cell| cell.glpyh }.join("")
- end
- end
- def tick
- any_changes = false
- all_cells = @world.flatten
- #---
- # Input
- #---
- all_cells.select { |cell| cell.type == :input }
- .each do |input|
- x, y = *input.position
- target = [x, y + 1]
- if get_cell(*target).empty?
- value = get_input
- unless value.nil?
- add_value(*target, value)
- any_changes = true
- end
- end
- end
- #---
- # Random
- #---
- all_cells.select { |cell| cell.type == :random }
- .each do |input|
- x, y = *input.position
- target = [x, y + 1]
- if get_cell(*target).empty?
- value = rand(10)
- unless value.nil?
- add_value(*target, value)
- any_changes = true
- end
- end
- end
- #---
- # Scanner
- #---
- all_cells.select { |cell| cell.type == :scanner }
- .each do |scanner|
- x, y = *scanner.position
- target = [x, y + 1]
- value = get_cell(*target)
- if value.is_a?(Value)
- add_output(value.value)
- end
- end
- #---
- # Furnace
- #---
- all_cells.select { |cell| cell.type == :furnace }
- .each do |furnace|
- x, y = *furnace.position
- (-1..1).map { |j| j + y }.each do |j|
- (-1..1).map { |i| i + x }.each do |i|
- next if j == y and i == x
- cell = get_cell(i, j)
- if cell.is_a?(Value)
- clear_cell(i, j)
- any_changes = true
- end
- end
- end
- end
- #---
- # Copiers
- #---
- all_cells.select { |cell| cell.type == :copy_up }
- .sort_by { |cell| cell.position[1] }
- .each do |copier|
- x, y = *copier.position
- origin = [x, y + 1]
- target = [x, y - 1]
- value = get_cell(*origin)
- if value.is_a?(Value) and get_cell(*target).empty?
- set_cell(*target, value)
- any_changes = true
- end
- end
- #---
- # Bulldozers
- #---
- all_cells.select { |cell| cell.type == :bulldozer_left }
- .sort_by { |cell| cell.position[0] }
- .each do |dozer|
- x, y = *dozer.position
- if get_cell(x - 1, y).is_a?(Value)
- if move_cell_horizontal(x - 1, y, -1)
- any_changes = true
- end
- end
- if move_cell_horizontal(x, y, -1)
- any_changes = true
- if get_cell(x - 1, y - 1).type == :turning_point
- flipped = Tile.new(:bulldozer_right, nil, "[", [x - 1, y])
- set_cell(x - 1, y, flipped)
- end
- end
- end
- all_cells.select { |cell| cell.type == :bulldozer_right }
- .sort_by { |cell| cell.position[0] }
- .each do |dozer|
- x, y = *dozer.position
- if get_cell(x + 1, y).is_a?(Value)
- if move_cell_horizontal(x + 1, y, 1)
- any_changes = true
- end
- end
- if move_cell_horizontal(x, y, 1)
- any_changes = true
- if get_cell(x + 1, y - 1).type == :turning_point
- flipped = Tile.new(:bulldozer_left, nil, "]", [x + 1, y])
- set_cell(x + 1, y, flipped)
- end
- end
- end
- #---
- # Belts
- #---
- all_cells.select { |cell| cell.type == :belt_left }
- .sort_by { |cell| cell.position[0] }
- .each do |belt|
- x, y = *belt.position
- if get_cell(x, y - 1).is_a?(Value)
- if move_cell_horizontal(x, y - 1, -1)
- any_changes = true
- end
- end
- end
- all_cells.select { |cell| cell.type == :belt_right }
- .sort_by { |cell| -cell.position[0] }
- .each do |belt|
- x, y = *belt.position
- if get_cell(x, y - 1).is_a?(Value)
- if move_cell_horizontal(x, y - 1, 1)
- any_changes = true
- end
- end
- end
- #---
- # Adders
- #---
- all_cells.select { |cell| cell.type == :adder }
- .each do |adder|
- x, y = *adder.position
- input_1 = get_cell(x, y - 1)
- input_2 = get_cell(x, y - 1)
- if input_1.is_a?(Value) and input_2.is_a?(Value)
- sum = input_1.value + input_2.value
- # TODO: decide where inputs go, and where outputs go
- any_changes = true
- end
- end
- # TODO: subtracters
- # TODO: doors that close their sides when there's anything above them
- # TODO: doors that close their sides when there's a value above them,
- # and they also decrease that value by one every time, and remove
- # it when it gets to 0
- return any_changes
- end
- end
- code = <<-END
- .T?.*.T.T.T
- F[ F 2]
- ....o......
- ...........
- END
- # code = <<-END
- # ..?..*..
- # . F
- # ..>>>>>.
- # END
- runner = VocalRunner.new(code, "test", [1, 0, 3, 2, 5, 4])
- runner.run
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement