Skip to content

Instantly share code, notes, and snippets.

@Ephasme
Last active August 29, 2015 14:24
Show Gist options
  • Select an option

  • Save Ephasme/e3494a07c22d400a51a6 to your computer and use it in GitHub Desktop.

Select an option

Save Ephasme/e3494a07c22d400a51a6 to your computer and use it in GitHub Desktop.
codingame.rb
STDOUT.sync = true # DO NOT REMOVE
# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.
module Utils
def letter_for(n)
return ' ' if n == 0
('A'.ord + n - 1).chr
end
def number_for(v)
return 0 if v == ' '
v = v.upcase.split(//).collect{|l| l.ord - 'A'.ord + 1}
return v.first if v.length == 1
v
end
def compute_distance(a, b, max)
delta = (b - a) % max
sign = delta<=>0
return 0 if delta == 0
delta -= (sign * max) unless (0..max/2).cover? delta.abs
delta
end
end
class Compiler
include Utils
attr_reader :tasks
def initialize
@machine = Machine.new
@tasks = []
@vars = {}
end
def store_letter_at(addr, letter)
move_to(addr)
store_letter(letter)
end
def save
[@machine.save, @tasks.join('|')]
end
def restore(save)
@machine.restore(save[0])
@tasks = save[1].split('|')
end
def move_to(addr)
delta = compute_distance(@machine.current_pointer, addr, Machine::MEMORY_SIZE)
eat delta.abs.times.collect{((delta<=>0) > 0) ? '>' : '<'}.join
end
def eat(byte_code)
@tasks << byte_code
@machine.execute(byte_code)
end
def store_letter(letter)
raise 'only one letter authorized' if letter.length > 1
store number_for letter
eat '.'
end
def repeat_word(word, n)
n_ptr = @machine.current_pointer
store (n-1)
eat '>'
store_word word
move_to n_ptr
valid_op = word.length.times.collect{'>.'}.join
return_op = word.length.times.collect{'<'}.join
eat ('[' << valid_op << return_op << '-' << ']')
end
def store(value)
delta = compute_distance(@machine.current_value, value, Machine::VALUES_SIZE)
eat delta.abs.times.collect{((delta<=>0) > 0) ? '+' : '-'}.join
end
def store_word_at(addr, word)
move_to addr
store_word word
end
def store_word(word)
letters = word.split(//)
letters[0..-2].to_a.each {|letter| store_letter(letter); eat '>'}
store_letter(letters.last)
end
end
class Machine
include Utils
MEMORY_SIZE = 30
VALUES_SIZE = 27
attr_reader :output
attr_reader :pointer
def save
[@memory.collect{|s| s.to_s}.join(','), @pointer, @cursor, @output, @loops]
end
def restore(_save)
@memory = _save[0].split(',').collect{|s| s.to_i}
@pointer = _save[1]
@cursor = _save[2]
@output = _save[3]
@loops = _save[4]
end
def self.parse(line)
tokens = line.split(//).collect do |letter|
case letter
when '+' then :plus
when '-' then :minus
when '>' then :right
when '<' then :left
when '.' then :validate
when '[' then :loop_start
when ']' then :loop_end
else
raise 'error'
end
end
balance_check(tokens)
tokens
end
def self.balance_check(tokens)
main_cursor = 0
the_end = tokens.length
while main_cursor != the_end
current_token = tokens[main_cursor]
if current_token == :loop_start
seek_cursor = main_cursor
balance = 0
while seek_cursor != the_end
balance += 1 if tokens[seek_cursor] == :loop_start
balance -= 1 if tokens[seek_cursor] == :loop_end
break if balance == 0
seek_cursor += 1
end
raise "Unbalanced loop #{main_cursor}#{seek_cursor}!" if balance != 0
tokens[main_cursor] = {name: :loop_start, value: {start: main_cursor, tail: seek_cursor}}
else
tokens[main_cursor] = {name: current_token, value: nil}
end
main_cursor += 1
end
end
def self.compile(tokens)
return tokens.collect do |token|
case token
when :plus then '+'
when :minus then '-'
when :right then '>'
when :left then '<'
when :validate then '.'
when :loop_start then '['
when :loop_end then ']'
end
end
end
def initialize
@memory = Array.new(MEMORY_SIZE, 0)
@pointer = 0
@cursor = 0
@output = []
@loops = []
end
def current_value
@memory[pointer]
end
def current_value=(value)
@memory[pointer] = (value % VALUES_SIZE)
end
def current_pointer=(v)
@pointer = (v % MEMORY_SIZE)
end
def current_pointer
@pointer
end
def plus
self.current_value += 1
end
def minus
self.current_value -= 1
end
def loop_start(**args)
@loops << args
@cursor = args[:tail] - 1 if current_value == 0
end
def loop_end
args = @loops.pop
raise 'Oops! Cursor move error!' if @cursor != args[:tail]
@cursor = args[:start] - 1 if current_value != 0
end
def left
self.current_pointer -= 1
end
def right
self.current_pointer += 1
end
def validate
@output << (letter_for current_value)
end
def execute(byte_code)
raise 'need string' unless byte_code.is_a? String
tokens = Machine::parse(byte_code)
while @cursor < tokens.length
case tokens[@cursor][:name]
when :plus then plus
when :minus then minus
when :right then right
when :left then left
when :validate then validate
when :loop_start then loop_start(tokens[@cursor][:value])
when :loop_end then loop_end
else
raise 'Not implemented'
end
@cursor += 1
end
@cursor = 0
byte_code
end
end
comp = Compiler.new
comp.repeat_word 'ALROG B', 26
s = comp.save
comp.repeat_word 'test', 5
comp.restore(s)
puts comp.tasks.join
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment