[2017][ruby][3.0]
change runxoopvuqvroovluwxlrpolvmzqkuxo
commit 320f74ade637e6ccc9e234da293d692c4d8dc317
author Alpha Chen <alpha.chen@gmail.com>
date
parent ywsowsvs
diff --git a/2017/ruby/day_03.rb b/2017/ruby/day_03.rb
index b3ce809..a4139af 100644
--- a/2017/ruby/day_03.rb
+++ b/2017/ruby/day_03.rb
@@ -1,40 +1,89 @@
-input = ARGF.read.to_i
-
-dir = [1,0]
-
-def next_dir(dir)
-  case dir
-  when [1,0]
-    [0,1]
-  when [0,1]
-    [-1,0]
-  when [-1,0]
-    [0,-1]
-  when [0,-1]
-    [1,0]
-  else
-    raise "invalid dir: #{dir}"
+require "minitest"
+
+class Spiral
+  def initialize
+    @square = [0,0]
+    @dir = [1,0]
+    @steps = [1, 1]
+  end
+
+  def each
+    return enum_for(__method__) unless block_given?
+
+    loop do
+      yield @square
+      step!
+    end
+  end
+
+  private
+
+  def step!
+    @steps[0] -= 1
+    @square = @square.zip(@dir).map {|i,j| i+j }
+    if @steps[0] == 0
+      change_dir!
+      @steps << next_steps
+      @steps.shift
+    end
   end
-end
 
-grid = Hash.new(0)
-grid[[0,0]] = 1
+  def change_dir!
+    @dir = case @dir
+           when [1,0]
+             [0,1]
+           when [0,1]
+             [-1,0]
+           when [-1,0]
+             [0,-1]
+           when [0,-1]
+             [1,0]
+           else
+             raise "invalid dir: #@dir"
+    end
+  end
 
-def next_value(grid, x, y)
-  [[-1, 1],[0, 1],[1,1],
-   [-1, 0],       [1,0],
-   [-1,-1],[0,-1],[1,-1]].map {|i,j| grid[[x+i,y+j]]}.sum
+  def next_steps
+    case @dir
+    when [1,0]
+      @steps[1]
+    when [0,1]
+      @steps[1] + 1
+    when [-1,0]
+      @steps[1]
+    when [0,-1]
+      @steps[1] + 1
+    else
+      raise "invalid dir: #@dir"
+    end
+  end
 end
 
-current = [0,0]
-loop do
-  next_square = current.zip(dir).map {|a,b| a + b }
-  value = next_value(grid, *next_square)
-  puts "#{current}: #{value}"
-  exit if value > input
-  grid[next_square] = value
-  current = next_square
-  dir = next_dir(dir) if grid[current.zip(next_dir(dir)).map {|a,b| a + b }] == 0
+if $0 == __FILE__
+  if ARGV.shift == "test"
+    require "minitest/test"
+  else
+    input = ARGF.read.to_i
+    p Spiral.new.each.with_index.find {|_,i| i == input - 1 }.first.sum
+  end
 end
 
-p next_value(grid, 1,0)
+class TestSpiral < Minitest::Test
+  def test_spiral
+    spiral = Spiral.new
+    e = spiral.each
+    assert_equal [0,0], e.next
+    assert_equal [1,0], e.next
+    assert_equal [1,1], e.next
+    assert_equal [0,1], e.next
+    assert_equal [-1,1], e.next
+    assert_equal [-1,0], e.next
+    assert_equal [-1,-1], e.next
+    assert_equal [0,-1], e.next
+    assert_equal [1,-1], e.next
+    assert_equal [2,-1], e.next
+
+    spiral = Spiral.new
+    assert_equal [0,-2], spiral.each.with_index.find {|_,i| i == 22 }.first
+  end
+end