@snoozer05 のボウリングスコア集計 Ruby 写経版を Squeak Smalltalk に逆翻訳

@snoozer05 なるほど。Ruby にない PositionableStream の動きは配列とカーソルの組み合わせで再現しましたか。すばらしいですね。

@sumim 12月15日


ということで、Squeak Smalltalk から Ruby への意訳である score1 を Squeak Smalltalk に逆翻訳。

| score1 |
score1 := [:pins |
   | cursor framePoints |
   cursor := 1.
   framePoints := OrderedCollection new.
   9 timesRepeat: [
      framePoints add: (pins copyFrom: cursor to: cursor + 2) asOrderedCollection.
      cursor := cursor + 2.
      framePoints last first = 10
         ifTrue: [cursor := cursor - 1]
         ifFalse: [
            ((framePoints last first: 2) reduce: #+) ~= 10
               ifTrue: [framePoints last removeLast]
         ]
   ].
   framePoints add: (pins last: pins size - cursor + 1).
   (framePoints collect: [:points | points reduce: #+]) reduce: #+
].

score1 value: #(10 10 10 10 10 10 10 10 10 10 10 10).  "=> 300 "
score1 value: #(10 10 10 10 10 10 10 10 10 3 3).  "=> 255 "
score1 value: #(0 0 10 8 2 10 10 10 5 3 8 2 10 2 3).  "=> 161 "
score1 value: #(5 3 7 2 8 2 10 7 1 9 0 6 2 10 6 4 8 0).  "=> 126 "


cursor を破壊的な操作で代替。

| score1bis |
score1bis := [:pins |
   | framePoints |
   pins := pins asOrderedCollection.
   framePoints := OrderedCollection new.
   9 timesRepeat: [
      framePoints add: (pins first: 3) asOrderedCollection.
      framePoints last first = 10
         ifTrue: [pins removeFirst: 1]
         ifFalse: [
            pins removeFirst: 2.
            (framePoints last first: 2) sum ~= 10
               ifTrue: [framePoints last removeLast]
         ]
   ].
   framePoints add: pins.
   (framePoints collect: #sum) sum
].

score1bis value: #(10 10 10 10 10 10 10 10 10 10 10 10).  "=> 300 "
score1bis value: #(10 10 10 10 10 10 10 10 10 3 3).  "=> 255 "
score1bis value: #(0 0 10 8 2 10 10 10 5 3 8 2 10 2 3).  "=> 161 "
score1bis value: #(5 3 7 2 8 2 10 7 1 9 0 6 2 10 6 4 8 0).  "=> 126 "


見通しをよくするために整理。

| score1bis2 |
score1bis2 := [:pins |
   | framePoints |
   pins := pins asOrderedCollection.
   framePoints := (1 to: 9) collect: [:idx |
      pins first = 10
         ifTrue: [(pins removeFirst: 1), (pins first: 2)]
         ifFalse: [pins first + pins second = 10
            ifTrue: [(pins removeFirst: 2), (pins first: 1)]
            ifFalse: [pins removeFirst: 2]]].
   framePoints := framePoints, {pins}.
   (framePoints collect: #sum) sum
].

score1bis2 value: #(10 10 10 10 10 10 10 10 10 10 10 10).  "=> 300 "
score1bis2 value: #(10 10 10 10 10 10 10 10 10 3 3).  "=> 255 "
score1bis2 value: #(0 0 10 8 2 10 10 10 5 3 8 2 10 2 3).  "=> 161 "
score1bis2 value: #(5 3 7 2 8 2 10 7 1 9 0 6 2 10 6 4 8 0).  "=> 126 "


参考のため、最後のを Ruby に変換。

def score1bis2(pins)
  framePoints = (1 .. 9).collect{ | idx |
    if pins.first == 10 then
      pins.shift(1) + pins.first(2)
    elsif pins[0] + pins[1] == 10
      pins.shift(2) + pins.first(1)
    else
      pins.shift(2)
    end
  }
  framePoints << pins
  framePoints.collect{ |ea| ea.inject(&:+) }.inject(&:+)
end

score1bis2 [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10] #=> 300
score1bis2 [10, 10, 10, 10, 10, 10, 10, 10, 10, 3, 3] #=> 255
score1bis2 [0, 0, 10, 8, 2, 10, 10, 10, 5, 3, 8, 2, 10, 2, 3] #=> 161
score1bis2 [5, 3, 7, 2, 8, 2, 10, 7, 1, 9, 0, 6, 2, 10, 6, 4, 8, 0] #=> 126