オフラインリアルタイムどう書くE01 Ruby で解く

## リンク

問題 http://nabetani.sakura.ne.jp/hena/orde01rotbk/
実装リンク集 http://qiita.com/Nabetani/items/4bf43031749c81c35526

## 実装

当日はテストが半分くらい落ちて[敗北](https://github.com/mtsmfm/sandbox/commit/7d173d84a092b9e779c694dacb1d2a6452245433)。
解説を聞いて飛び石に気づき、[教わった chunk 使ってやっとテスト突破](https://github.com/mtsmfm/sandbox/commit/0ab8ea2969182d7d41ff4921672780d5d719bd10)。

強いて他の人との差別点を挙げるなら、 ActiveSupport の `Array#from` と `Object#presence` くらい。
transpose なのに transpose してないのはご愛嬌。
(当日は 90 度回転とか transpose でよゆーとか思ってた。 reverse すればよかったのね。)

```ruby
require 'bundler'
Bundler.require

class Field
  attr_reader :field

  def initialize(size)
    @field = Array.new(size) { [nil] * size }
  end

  def fill_by(group)
    group.each do |size|
      fill(*detect_space, size)
    end
  end

  def fill(x, y, size)
    (x...(x + size)).each do |_x|
      (y...(y + size)).each do |_y|
        @field[_y][_x] = size
      end
    end
  end

  def detect_space
    y = @field.find_index(@field.detect {|row| row.include?(nil) })
    x = @field[y].find_index(nil)
    [x, y]
  end

  def inspect
    @field.map do |row|
      row.map {|column| "%03d" % column }.join(",")
    end.join("\n")
  end

  def to_s
    inspect
  end

  def size
    @field.size
  end

  def transpose
    field = Field.new(size)

    @field.each.with_index do |row, y|
      row.each.with_index do |column, x|
        field.field[x][size - y - 1] = column
      end
    end

    field
  end
end

def solve(input)
  ans_key, groups = input.match(/(\d+):(.*)/).captures
  groups = groups.scan(/\((.*?)\)/).flatten.map {|group| group.split(?,).map(&:to_i) }

  field = Field.new(groups.first.sum)
  groups.each do |group|
    field.fill_by(group)
  end
  groups = field.transpose.field.uniq.map(&:uniq)
  g = groups.map.with_index {|group, i| group.chunk {|e| (groups - groups.from(i)).flatten.exclude?(e).presence }.map(&:last) }.flatten(1)[ans_key.to_i - 1]
  "(#{g.join(?,)})"
end

def test(input, expect)
  actual = solve(input)

  if actual == expect
    puts "OK: #{input}"
  else
    raise "NG: #{input} : expect #{expect} but got #{actual}"
  end
end

DATA.each_line do |line|
  puts line
  eval line.match(%r{.*(test.*)}).captures.first
end

__END__
/*0*/ test( "4:(55,44,48)(40,4)(52)(26,29)(23,3)(20,31,21)(5,47)(43)(9,17)(1,8)(32)(25)", "(32,31)" );
/*1*/ test( "6:(33,29,50)(4,25)(37)(15,35)(16,9)(7,2)(17)(42,18)(6,11)(8,27)(24)(19)", "(6,17,2)" );
/*2*/ test( "7:(60,50)(23,27)(24,22,14)(7,16)(8,6)(12,15)(13)(2,28)(26)(4,21,3)(18)(17)", "(4,16)" );
/*3*/ test( "6:(99,73,56)(17,39)(68,22)(36,25)(57,42)(9,16)(2,7)(10,28)(23)(15,87,18)(72)(69)", "(10,36,22)" );
/*4*/ test( "7:(79,49,66,63)(32,17)(3,60)(29,57)(55,22,2)(20,14)(15,28)(33,9)(24)(11,134)(123)", "(29,17)" );
/*5*/ test( "7:(159,129)(57,72)(56,39,25,16,23)(9,7)(2,28)(36)(42,15)(17,22)(87)(10,18)(73)(68)(60)", "(10,28)" );
/*6*/ test( "14:(113,71,68)(32,36)(42,29)(13,44,4)(40)(62,37,38,31)(7,108)(25,12)(11,34)(23)(77,10)(67)", "(36)" );
/*7*/ test( "8:(145,125)(53,72)(45,11,9,16,31,33)(2,7)(13)(8,15)(21)(14,32)(30,37,19)(80)(18,73)(62)(55)", "(31)" );
/*8*/ test( "1:(175,140,164)(35,29,52,24)(28,160)(6,23)(130,86)(43,60)(26,17)(77)(44,68)(174)(5,155)(150)", "(174,130,175)" );
/*9*/ test( "6:(240,168,187)(149,19)(206)(163,77)(86,82,58)(40,18)(22,202)(4,78)(192,61)(62)(48,13)(35,118)(83)", "(4,82)" );
/*10*/ test( "5:(100,73,59)(14,45)(56,31)(58,42)(25,51)(19,36,26)(16,18,8)(2,17)(10)(77)(74)(28)(23,30)(44,7)(37)", "(2,19,56,73)" );
/*11*/ test( "8:(100,88,76)(27,49)(12,19,39,18)(95,11,6)(3,24)(5,1)(21)(20)(16)(2,47)(77,45)(92)(69,26)(17,60)(43)", "(19)" );
/*12*/ test( "11:(262,196,203)(83,106,7)(210)(161,84,17)(36,41,23)(18,111)(31,5)(64)(77,38)(102)(73,248)(238)(175)", "(248)" );
/*13*/ test( "9:(117,79,74)(5,69)(84)(71,46)(20,49)(25,39,57,29)(82,14)(78)(35,18)(13,12,50)(1,11)(4,10)(33,6)(27)", "(1,12)" );
/*14*/ test( "7:(163,95,78)(17,61)(68,44)(24,81)(89,94,72)(15,66)(42,45)(84,5)(79,20)(59,3)(26,22)(16,50)(4,34)(30)", "(30,26,3)" );
/*15*/ test( "10:(100,95,69)(26,43)(11,16,77,17)(88,12)(6,5)(1,20)(19)(60)(39)(18,21)(45,92)(76,27,3)(24)(49,2)(47)", "(47,2)" );
/*16*/ test( "8:(120,78,102)(34,20,24)(14,6)(2,10,23,91)(8)(53,13)(75,45)(36)(4,32)(30,47,25)(22,3)(19,107)(105)(88)", "(4,36,13)" );
/*17*/ test( "13:(119,124,96)(28,68)(114,5)(117,40)(56,52)(4,48)(21,15,24)(106,8)(6,9)(98,38,16)(13,20)(22,7)(75)(60)", "(20)" );
/*18*/ test( "2:(302,277,246)(57,189)(25,117,109,26)(235,92)(83)(8,135,49)(90,127)(81,157)(53,37)(5,76)(304)(288)(233)", "(53,90,92)" );
/*19*/ test( "13:(158,109,240)(49,60)(114,82,11)(71)(126,267)(62,52)(10,42)(40,32)(8,51,141)(48)(14,37)(39,9)(23)(7,53)(46)", "(60)" );
/*20*/ test( "8:(132,113,251)(19,36,58)(107,27,17)(10,21,22)(25,12)(1,20)(13)(80)(32,6)(26)(23,35)(130)(121,245)(127,3)(124)", "(26,6)" );
/*21*/ test( "4:(187,127,194)(60,67)(131,76,40)(33,72,156)(44,29)(19,10)(55,21)(82)(13,27,4)(23)(34)(50)(190,30)(160,2)(158)", "(60,127)" );
/*22*/ test( "12:(239,245)(132,107)(124,121)(27,25,32,23)(3,118)(35,115)(113,19)(12,13)(17,10)(6,26)(21,1)(20)(36)(22,80)(58)", "(124,245)" );
/*23*/ test( "3:(176,152)(24,38,90)(80,63,43,14)(52)(20,23)(17,26,40)(37,42,86)(72,25)(16,10)(6,4)(49,19,8,5)(47)(3,44)(11)(30)", "(17,63)" );
/*24*/ test( "15:(171,181)(95,76)(93,88)(19,30,27)(42,40,32)(5,83)(3,15,29,78)(21,12)(9,18)(8,54)(4,25)(2,46)(22)(44)(1,24)(23)", "(93,181)" );
/*25*/ test( "13:(152,88,112)(64,24)(44,92)(200,12,4)(8,7,33)(1,6)(16,5)(11)(27)(18,15)(3,31,73)(29,19)(10,9)(40)(39)(77,2)(75)", "(3,15)" );
/*26*/ test( "13:(484,316,379)(108,145,63)(82,360)(42,66)(29,198)(18,24)(308,194)(119)(69,50)(168,80)(114,149)(440)(387,35)(352)", "(379)" );
/*27*/ test( "12:(181,191)(93,88)(24,23,54,46,44)(1,22)(25)(2,42)(4,18)(8,40)(29)(9,21,32)(15,12)(3,30)(5,103,27)(98)(19,95)(76)", "(21)" );
/*28*/ test( "18:(83,79,123)(35,44)(52,31)(21,36,9)(27,60,89)(48,25)(10,20,33)(23,12)(2,5,13)(11,3)(8)(102,49,45)(16,73)(4,57)(53)", "(123)" );
/*29*/ test( "4:(649,439,456)(214,208,17)(473)(6,55,147)(385,260,4)(175,49)(104)(12,135)(116)(81,94)(125,216)(203,7)(615)(510)(419)", "(81,175,4)" );
/*30*/ test( "17:(100,114,48,53)(43,5)(58)(23,20)(61,25,14)(3,75)(11,47,96)(36)(95,49)(24,51)(37,105,27)(78)(9,28)(59,26,19)(7,40)(33)", "(58,5)" );
```

```ruby
source 'https://rubygems.org'

gem 'activesupport', require: 'active_support/all'
gem 'pry'
gem 'tapp'
gem 'awesome_print'
```

```ruby
GEM
  remote: https://rubygems.org/
  specs:
    activesupport (4.2.5.1)
      i18n (~> 0.7)
      json (~> 1.7, >= 1.7.7)
      minitest (~> 5.1)
      thread_safe (~> 0.3, >= 0.3.4)
      tzinfo (~> 1.1)
    awesome_print (1.6.1)
    coderay (1.1.0)
    i18n (0.7.0)
    json (1.8.3)
    method_source (0.8.2)
    minitest (5.8.4)
    pry (0.10.3)
      coderay (~> 1.1.0)
      method_source (~> 0.8.1)
      slop (~> 3.4)
    slop (3.6.0)
    tapp (1.5.0)
      thor
    thor (0.19.1)
    thread_safe (0.3.5)
    tzinfo (1.2.2)
      thread_safe (~> 0.1)

PLATFORMS
  ruby

DEPENDENCIES
  activesupport
  awesome_print
  pry
  tapp

BUNDLED WITH
   1.11.2
```