「オフラインリアルタイムどう書く第9回(バス代)」の問題を解いてみた


[横浜へなちょこプログラミング勉強会](https://yhpg.doorkeeper.jp/)にて過去に出題された[バス代](http://nabetani.sakura.ne.jp/hena/ord9busfare/)を解いてみた。
回答にかかった時間は15分程度。

今回は特に考えることもなかったのであっさりと解くことが出来た。
幼児2名までを無料とする部分が気に食わないが、コードが短くなる代わりにループ回数が増えてしまう実装ばかりが思い浮かんだのでとりあえずこのままとする。

```ruby
class BusFare
  attr_reader :fare
  BASE_FARE_RATE_TABLE = {
    A: 1,
    C: 0.5,
    I: 0.5
  }
  FARE_RATE_TABLE = {
    n: 1,
    w: 0.5,
    p: 0
  }

  def initialize input
    fare, passengers = input.split ":"
    @section_fare = fare.to_i
    @passengers  = passengers.split ","
    @fare = 0
  end

  def calc
    passengers = @passengers.dup
    passengers.select{|pas| pas[0] == "A"}.size.times{
      2.times{
        if index = passengers.index("In")
          passengers.delete_at index
        elsif index = passengers.index("Iw")
          passengers.delete_at index
        end
      }
    }
    @fare = passengers.inject(0){|f, pas|
      f + fraction_up(fraction_up(@section_fare * BASE_FARE_RATE_TABLE[pas[0].to_sym]) * FARE_RATE_TABLE[pas[1].to_sym])
    }
  end

  private

  def fraction_up n
    ((n % 10 > 0) ? (n - (n % 10)) + 10 : n).to_i
  end
end

test = <<_TEST
/*0*/ test( "210:Cn,In,Iw,Ap,Iw", "170" );
/*1*/ test( "220:Cp,In", "110" );
/*2*/ test( "230:Cw,In,Iw", "240" );
/*3*/ test( "240:In,An,In", "240" );
/*4*/ test( "250:In,In,Aw,In", "260" );
/*5*/ test( "260:In,In,In,In,Ap", "260" );
/*6*/ test( "270:In,An,In,In,Ip", "410" );
/*7*/ test( "280:Aw,In,Iw,In", "210" );
/*8*/ test( "200:An", "200" );
/*9*/ test( "210:Iw", "60" );
/*10*/ test( "220:Ap", "0" );
/*11*/ test( "230:Cp", "0" );
/*12*/ test( "240:Cw", "60" );
/*13*/ test( "250:In", "130" );
/*14*/ test( "260:Cn", "130" );
/*15*/ test( "270:Ip", "0" );
/*16*/ test( "280:Aw", "140" );
/*17*/ test( "1480:In,An,In,In,In,Iw,Cp,Cw,In,Aw,In,In,Iw,Cn,Aw,Iw", "5920" );
/*18*/ test( "630:Aw,Cw,Iw,An,An", "1740" );
/*19*/ test( "340:Cn,Cn,Ip,Ap", "340" );
/*20*/ test( "240:Iw,Ap,In,Iw,Aw", "120" );
/*21*/ test( "800:Cw,An,Cn,Aw,Ap", "1800" );
/*22*/ test( "1210:An,Ip,In,Iw,An,Iw,Iw,An,Iw,Iw", "3630" );
/*23*/ test( "530:An,Cw,Cw", "810" );
/*24*/ test( "170:Aw,Iw,Ip", "90" );
/*25*/ test( "150:In,Ip,Ip,Iw,In,Iw,Iw,In,An,Iw,Aw,Cw,Iw,Cw,An,Cp,Iw", "580" );
/*26*/ test( "420:Cn,Cw,Cp", "320" );
/*27*/ test( "690:Cw,In,An,Cp,Cn,In", "1220" );
/*28*/ test( "590:Iw,Iw,Cn,Iw,Aw,In,In,Ip,Iw,Ip,Aw", "1200" );
/*29*/ test( "790:Cw,Cn,Cn", "1000" );
/*30*/ test( "1220:In,In,An,An,In,Iw,Iw,In,In,Ip,In,An,Iw", "4590" );
/*31*/ test( "570:Cw,Cn,Cp", "440" );
/*32*/ test( "310:Cn,Cw,An,An,Iw,Cp,Cw,Cn,Iw", "1100" );
/*33*/ test( "910:Aw,In,Iw,Iw,Iw,Iw,Iw,An,Cw,In", "2290" );
/*34*/ test( "460:Iw,Cw,Cw,Cn", "590" );
/*35*/ test( "240:Iw,Iw,In,Iw,In,In,Cn,In,An", "780" );
/*36*/ test( "1240:In,In,In,Ap,In,Cw,Iw,Iw,Iw,Aw,Cw", "2170" );
/*37*/ test( "1000:Iw,Ip,In,An,In,In,In,An,In,Iw,In,In,Iw,In,Iw,Iw,Iw,An", "5500" );
/*38*/ test( "180:In,Aw,Ip,Iw,In,Aw,In,Iw,Iw,In", "330" );
/*39*/ test( "440:In,Ip,Cp,Aw,Iw,In,An", "660" );
/*40*/ test( "1270:Ap,In,An,Ip,In,Ip,Ip", "1270" );
_TEST

require 'minitest/autorun'

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