「オフラインリアルタイムどう書く第28回(十字の壁がそそり立つ世界の中を君は螺旋状に歩く)」の問題を解いてみた


[横浜へなちょこプログラミング勉強会](https://yhpg.doorkeeper.jp/)にて過去に出題された[十字の壁がそそり立つ世界の中を君は螺旋状に歩く](http://nabetani.sakura.ne.jp/hena/ord28spirwa/)を解いてみた。
回答にかかった時間は90分程度。

別実装を30分程度で書いたのだが、それだと追加問題に対応できなかった。
追加問題にも対応するため再度実装しなおしたのがこの実装となる。
追加問題についても答えが載っていたので、他のテストケースと同じ形式に直してテストケースに追加する形を取っている。
`CrossWallWorld#rounding`の`each`内で`break`することで本来`self`が返ってくるはずなのに数値を返したり、`CrossWallWorld#go`の`inject`で数字を回しておきながら`break`で`Symbol`を返すというなんとも気持ち悪い実装をしている。
読み返してもやっぱり気持ち悪いのだが、他にいいやり方も思いつかないのでこのままにしている。

```ruby
class CrossWallWorld
  def travel input
    wall, days = input.split ":"
    @north, @east, @south, @west = wall.split(",").map &:to_i
    go *rounding(days.to_i)
  end

  private

  def rounding days
    c = (1..Float::INFINITY).each{|c, _|
      r = @north * 2 + @east * 2 + @south * 2 + @west * 2 + c * 8
      break c if days < r
      days -= r
    }
    [days, c]
  end

  def go days, count
    [
      [@east,  :E], [count * 2, :S], [@east,      :W],
      [@south, :S], [count * 2, :W], [@south,     :N],
      [@west,  :W], [count * 2, :N], [@west,      :E],
      [@north, :N], [count * 2, :E], [@north - 1, :S],
      [@east,  :E]
    ].inject(0){|d, (len, way)|
      break way if days < (d + len)
      d + len
    }.to_s
  end
end

test = <<_TEST
/*0*/ test( "2,3,5,4:85", "S" );    
/*1*/ test( "1,2,3,4:1", "E" );    
/*2*/ test( "1,2,3,4:2", "S" );    
/*3*/ test( "1,2,3,4:3", "S" );    
/*4*/ test( "1,2,3,4:4", "W" );    
/*5*/ test( "1,2,3,4:27", "E" );    
/*6*/ test( "1,2,3,4:63", "E" );    
/*7*/ test( "1,2,3,4:40", "W" );    
/*8*/ test( "1,4,3,2:40", "S" );    
/*9*/ test( "3,3,3,3:30", "S" );    
/*10*/ test( "3,3,3,3:31", "E" );    
/*11*/ test( "3,3,3,3:32", "E" );    
/*12*/ test( "3,3,3,3:70", "S" );    
/*13*/ test( "3,3,3,3:71", "E" );    
/*14*/ test( "3,3,3,3:72", "E" );    
/*15*/ test( "1,1,1,1:7", "N" );    
/*16*/ test( "1,2,1,1:7", "W" );    
/*17*/ test( "1,6,1,1:7", "S" );    
/*18*/ test( "1,8,1,1:7", "E" );    
/*19*/ test( "1,1,1,1:30", "N" );    
/*20*/ test( "1,2,1,1:30", "W" );    
/*21*/ test( "1,5,1,1:30", "S" );    
/*22*/ test( "1,8,1,1:30", "E" );    
/*23*/ test( "9,9,9,9:99", "W" );    
/*24*/ test( "5,6,3,8:3", "E" );    
/*25*/ test( "5,8,1,1:11", "W" );    
/*26*/ test( "2,8,1,2:18", "S" );    
/*27*/ test( "3,2,3,1:20", "N" );    
/*28*/ test( "3,3,8,1:28", "N" );    
/*29*/ test( "2,5,1,2:32", "E" );    
/*30*/ test( "2,5,1,6:33", "E" );    
/*31*/ test( "1,2,5,7:34", "N" );    
/*32*/ test( "3,6,5,6:36", "E" );    
/*33*/ test( "6,2,8,1:39", "S" );    
/*34*/ test( "3,1,2,3:41", "W" );    
/*35*/ test( "1,1,3,4:45", "W" );    
/*36*/ test( "1,3,1,2:46", "N" );    
/*37*/ test( "4,4,4,4:49", "W" );    
/*38*/ test( "3,1,4,4:55", "N" );    
/*39*/ test( "6,6,2,1:56", "W" );    
/*40*/ test( "3,2,1,2:59", "S" );    
/*41*/ test( "2,7,7,1:60", "S" );    
/*42*/ test( "3,1,1,1:63", "N" );    
/*43*/ test( "4,6,4,1:78", "E" );    
/*44*/ test( "7,5,3,6:79", "W" );    
/*45*/ test( "7,8,3,1:81", "E" );    
/*46*/ test( "3,2,5,2:82", "S" );    
/*47*/ test( "1,1,3,4:84", "N" );    
/*48*/ test( "7,4,1,5:88", "S" );    
/*49*/ test( "3,6,5,3:89", "S" );    
/*50*/ test( "1,4,2,3:92", "N" );    
/*51*/ test( "1,3,4,5:93", "W" );    
/*52*/ test( "2,4,8,1:94", "W" );    
/*53*/ test( "3,6,1,7:99", "S" );
/*54*/ test( "1234,2345,3456,4567:978593417", "E" );
/*55*/ test( "1234,2345,3456,4567:978593418", "S" );
/*56*/ test( "31415,92653,58979,32384:9812336139", "W" );
/*57*/ test( "31415,92653,58979,32384:9812336140", "S" );
/*58*/ test( "314159,265358,979323,84626:89099331642", "S" );
/*59*/ test( "314159,265358,979323,84626:89099331643", "W" );
_TEST

require 'minitest/autorun'

describe 'CrossWallWorld' do
  test.split("\n").each do |line|
    t, n, input, expect = line.match(/^\/\*(\d+)\*\/\s*test\(\s*"([^"]+)",\s*"([^"]+)"\s*\);*\s*$/).to_a
    world = CrossWallWorld.new
    it input do
      assert_equal expect, world.travel(input)
    end
  end
end
```