Last active
August 29, 2015 14:24
-
-
Save Ephasme/e3494a07c22d400a51a6 to your computer and use it in GitHub Desktop.
codingame.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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