オフラインリアルタイムどう書く問題作成リハビリ #0 Ruby で解く

問題はこちら:
https://qiita.com/Nabetani/items/03f983f0835b928a9524
https://nabetani.github.io/q/pages/r0/

解くのに2時間くらいかかりました。テストが通るのに1分かかります。

斜めである必要性ないのでは? と解いていたら意味があることに気づかされたり、数字が重複し得ることもあとで気づいたりとコードはひどいものの楽しく解かせていただきました。

何も考えずに実装してあとでオブジェクト作って無理やり数えたりとだいぶひどいコードですが、それもどう書くの醍醐味だなとテスト通った初版そのまま置いておきます。

```ruby
require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'

  gem 'activesupport', require: 'active_support/all'

  gem 'minitest', require: 'minitest/autorun'
  gem 'minitest-reporters'

  gem 'awesome_print'
  gem 'tapp'

  gem 'pry'
  gem 'pry-rescue', require: 'pry-rescue/minitest'
  gem 'pry-stack_explorer'
end

class Value
  attr_reader :v

  def initialize(v)
    @v = v
  end
end

class Board
  def initialize
    @rows = Hash.new {|h, k| h[k] = []}
    @max_v = 0
    @max_offset = nil
  end

  def insert(v)
    (0..).each do |n|
      (0..n).each do |nn|
        offset_x, offset_y = nn, n - nn

        if available?(offset_x, offset_y, v)
          fill(offset_x, offset_y, v)
          if @max_v < v
            @max_v = v
            @max_offset = [offset_x, offset_y]
          end
          return
        end
      end
    end
  end

  def around_max
    result = Set.new

    x_range = [@max_offset[0]-1, 0].max..(@max_offset[0] + @max_v)
    y_range = [@max_offset[1]-1, 0].max..(@max_offset[1] + @max_v)

    # left right
    x_range.minmax.each do |x|
      y_range.each do |y|
        next if y == y_range.min || y == y_range.max
        if @rows[x][y] && @rows[x][y].v != @max_v
          result << @rows[x][y]
        end
      end
    end

    # top bottom
    y_range.minmax.each do |y|
      x_range.each do |x|
        next if x == x_range.min || x == x_range.max
        if @rows[x][y] && @rows[x][y].v != @max_v
          result << @rows[x][y]
        end
      end
    end

    result.map(&:v).sort.to_a
  end

  def fill(offset_x, offset_y, v)
    value = Value.new(v)

    v.times do |x|
      v.times do |y|
        @rows[x + offset_x][y + offset_y] = value
      end
    end
  end

  def available?(offset_x, offset_y, v)
    v.times.all? do |x|
      v.times.all? do |y|
        !@rows[x + offset_x][y + offset_y]
      end
    end
  end

  def inspect
    lines = []

    "\n" + (0..max_y).to_a.reverse.map do |y|
      (0..max_x).map do |x|
        @rows[x][y]&.v || "-"
      end.join("")
    end.join("\n")
  end

  private

  def max_x = @rows.keys.max || 0
  def max_y = @rows.values.map(&:size).max || 0
end

def solve(input)
  input_data = input.split(",").map(&:to_i)
  board = Board.new
  input_data.each do |x|
    board.insert(x)
  end

  board.around_max.join(",")
end

TEST_DATA = <<~EOS
/*0*/ test( "27,17,8,36,16,12,11,13,25,23,10,6,2,9", "2,8,9,10,13,23,27" );
/*1*/ test( "1,2,3", "2" );
/*2*/ test( "4,3,5,2,1", "1,4" );
/*3*/ test( "3,2,1,4,3,2,1", "1,1,2,3" );
/*4*/ test( "9,8,7,12,11,10,2,5", "5,7,9,10,11" );
/*5*/ test( "6,2,2,4,4,2,2,7,4,4,1,5", "2,4,4,4,6" );
/*6*/ test( "5,25,6,24,7,23,8,22,9,21", "5,6,7,8,24" );
/*7*/ test( "6,6,6,14,3,3,1,2,15,7,8,9", "3,8,9" );
/*8*/ test( "6,7,8,9,6,7,8,9,10,6,7,8,9", "7,9,9" );
/*9*/ test( "1,1,2,3,5,8,13,21,34,55,89,144", "55,89" );
/*10*/ test( "144,89,55,34,21,13,8,5,3,2,1,1", "34,55,89" );
/*11*/ test( "5,3,4,2,25,6,24,7,23,8,22,9,2,1", "1,3,5,6,7,8,24" );
/*12*/ test( "2,9,9,9,3,8,8,8,10,7,7,7,6,6,5,5", "6,8,9" );
/*13*/ test( "1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1", "5,6,7,8,8" );
/*14*/ test( "1,6,6,6,2,6,6,6,3,6,6,6,7,6,6,6,4,5", "3,5,6,6,6" );
/*15*/ test( "4,4,4,5,5,5,6,6,6,6,7,7,5,9,5,4,4,8", "5,5,6,6,8" );
/*16*/ test( "5,4,3,40,9,8,7,6,5,4,3,2,1,5,4,3,2,1", "1,1,3,3,3,5,5,5,6,8,9" );
/*17*/ test( "83,26,119,18,32,57,41,98,113,120,28,20", "41,98,119" );
/*18*/ test( "10,12,25,14,11,16,10,17,5,5,15,19,13,18", "5,10,11,12,14,17" );
/*19*/ test( "1,3,5,7,9,11,13,15,17,16,14,12,10,8,6,4,2", "10,12,13,15,16" );
/*20*/ test( "12,87,40,44,43,59,83,35,97,29,17,39,84,24", "24,43,87" );
/*21*/ test( "20,21,22,23,24,25,26,27,28,29,6,3,3,3,3,3", "3,6,27,28" );
/*22*/ test( "5,15,6,16,7,17,8,18,9,19,10,20,9,10,11,12", "10,12,19" );
/*23*/ test( "5,5,5,4,9,4,5,5,3,7,6,6,8,2,3,2,8,2,2,6,5", "2,3,5,5,6,7" );
/*24*/ test( "94,93,104,120,36,88,67,37,83,92,25,103,75,22", "22,92,93,103" );
/*25*/ test( "16,15,14,13,12,11,10,29,28,27,26,25,24,4,10,9", "4,9,11,15,26,27" );
/*26*/ test( "84,87,21,18,83,119,17,28,37,88,13,91,49,76,24", "76,83,87,88" );
/*27*/ test( "41,16,26,94,68,74,21,116,50,31,47,86,39,94,106", "39,68,94,106" );
/*28*/ test( "70,40,67,49,105,59,117,42,89,83,73,15,96,57,17", "73,96,105" );
/*29*/ test( "8,9,10,11,12,13,14,15,16,17,18,6,5,4,3,7,6,5,4", "4,6,15,16" );
/*30*/ test( "9,8,7,20,6,6,5,5,4,4,3,3,2,2,1,1,10,11,12,6,5,4", "1,1,2,3,3,5,6,7,9,12" );
/*31*/ test( "15,15,15,5,14,6,7,8,9,10,11,12,13,14,15,16,17,18", "16,17" );
/*32*/ test( "70,80,81,42,77,30,53,43,120,77,108,53,16,44,43,28", "43,43,53,77,108" );
/*33*/ test( "88,107,125,102,130,24,38,29,105,63,58,53,109,98,94", "63,94,98,107,125" );
/*34*/ test( "96,119,112,20,16,46,30,76,112,57,105,56,107,51,53,26", "26,30,46,57,96,112" );
/*35*/ test( "110,112,117,56,60,120,16,86,47,96,85,84,75,60,72,68,38", "68,75,84,117" );
/*36*/ test( "98,52,17,78,34,120,109,62,46,31,22,43,64,110,105,51,1,2,3", "22,43,52,62,98,105" );
/*37*/ test( "2,5,11,17,23,31,41,47,59,67,73,71,61,53,43,37,29,19,13,7,3", "43,53,59,67,71" );
/*38*/ test( "13,62,74,40,51,16,27,81,43,15,120,15,56,16,101,20,35,102,73,21", "21,51,73,102" );
/*39*/ test( "64,94,98,119,25,36,115,14,44,13,40,92,50,46,62,53,66,118,32,27", "27,40,46,92,94,118" );
/*40*/ test( "95,86,41,115,73,41,24,87,101,72,102,77,81,47,66,66,99,18,39,13", "18,41,41,87,95,101,102" );
/*41*/ test( "22,118,89,80,45,120,85,37,69,96,20,13,118,45,74,92,38,91,87,111", "38,45,74,89,91" );
/*42*/ test( "82,46,118,13,85,103,80,28,86,89,54,92,15,94,69,93,66,60,22,14,15", "15,22,80,82,89,103" );
/*43*/ test( "42,37,24,18,19,33,16,6,11,27,17,25,7,9,35,48,49,50,15,8,4,2,30,29", "15,25,29,35" );
/*44*/ test( "43,36,58,54,84,116,68,95,61,22,16,14,68,25,109,37,66,74,31,43,75,49", "31,54,61,66,75,95" );
/*45*/ test( "126,22,76,25,32,45,37,148,47,56,17,146,56,72,78,54,32,141,21,46,140,83", "32,54,76,78,83,126" );
/*46*/ test( "85,100,122,117,107,68,63,22,130,63,57,49,71,70,117,82,14,51,59,68,69,116", "68,68,116,122" );
/*47*/ test( "69,52,123,101,65,27,22,103,99,133,152,25,93,34,55,36,81,29,112,28,83,46,127,27,43", "46,103,133" );
/*48*/ test( "97,39,129,49,86,94,153,85,28,96,96,140,82,107,140,106,91,106,124,147,95,144,89,60,52", "82,106,129,140" );
/*49*/ test( "127,52,46,85,104,147,148,93,76,55,143,106,38,22,66,143,85,113,145,81,138,38,23,29,19,20,16,10", "10,16,20,29,104,106,143,145,147" );
/*50*/ test( "132,88,143,97,156,149,133,119,138,89,148,18,104,115,37,50,90,91,125,145,111,134,64,149,124,28", "18,28,89,97,104,119,143" );
/*51*/ test( "156,41,97,44,167,114,98,83,130,51,55,78,134,39,95,104,147,43,98,126,36,156,66,129,24,97,21,8,7", "8,21,44,55,83,98,134,156" );
/*52*/ test( "48,47,146,39,56,49,19,128,151,27,24,150,73,167,80,58,34,85,148,23,160,138,34,74,61,120,58,158,117", "58,61,117,128,151,160" );
/*53*/ test( "134,162,87,47,160,93,199,125,117,69,45,198,40,35,197,51,55,96,147,145,90,73,72,71,70,146,26,33,7,24", "26,33,51,55,69,145,147,160,162" );
/*54*/ test( "73,147,153,172,171,70,97,20,124,64,44,17,60,45,131,26,43,173,111,172,25,84,147,52,68,19,40,48,38,37", "26,37,38,48,172" );
EOS

Minitest::Reporters.use!(Minitest::Reporters::ProgressReporter.new)

# docker-compose run --rm -w /app/YYYYmmdd bundle exec ruby doukaku.rb -n /#1$/
describe 'Doukaku' do
  def self.test_order; :sorted; end

  TEST_DATA.each_line do |test|
    number, input, expected = test.scan(/(\d+).*"(.*)", "(.*)"/)[0]

    it "##{number}" do
      assert_equal expected, solve(input)
    end
  end
end
```

Gemfile.lock を含む完全版はこちら。

https://github.com/mtsmfm/doukaku/blob/8592002d7cf484f7bdbe4c7295add948905821b4/20230424/doukaku.rb