Home Reference Source

src/bowling/index.js

'use strict';

const maxFrames = 10;

class BowlingGame {
  constructor() {
    /**
     * @private
     * @type {Array}
     */
    this.frames = [];
  }


  /**
   * @private
   * @param  {Array} frame
   * @return {Boolean}
   */
  static isSpare(frame) {
    return !BowlingGame.isStrike(frame) && 2 === frame.length && 10 === (frame[0] + frame[1]);
  }

  /**
   * @private
   * @param  {Array} frame
   * @return {Boolean}
   */
  static isStrike(frame) {
    return 2 === frame.length && 10 === frame[0] && 0 === frame[1];
  }

  /**
   * @private
   * @param  {Array} frame
   * @return {Number}
   */
  static frameScore(frame) {
    return frame.reduce((sum, score) => sum + score, 0);
  }

  /**
   * @param  {Number} pins - Number of pins knocked down
   * @throws {TypeError}
   * @return {undefined}
   */
  roll(pins) {
    if (!Number.isInteger(pins)) {
      throw new TypeError('Argument pins must be of typ integer.');
    }

    if (10 === pins) {
      this.frames.push([pins, 0]);
    } else if (0 === this.frames.length) {
      this.frames.push([pins]);
    } else if (2 <= this.frames[this.frames.length - 1].length) {
      this.frames.push([pins]);
    } else {
      this.frames[this.frames.length - 1].push(pins);
    }
  }

  /**
   * @return {Number}
   */
  score() {
    return this.frames.reduce((acc, frame, index, frames) => {
      const nextFrame = undefined !== frames[index + 1]
              ? frames[index + 1]
              : [],
            followingFrame = undefined !== frames[index + 2]
              ? frames[index + 2]
              : [];

      let score = BowlingGame.frameScore(frame);

      if (BowlingGame.isStrike(frame)) {
        score += nextFrame[0];

        if (BowlingGame.isStrike(followingFrame)) {
          score += followingFrame[0];
        }
      }

      if (BowlingGame.isSpare(frame)) {
        score += BowlingGame.frameScore(nextFrame);
      }

      if (maxFrames >= index + 1) {
        return acc + score;
      }

      return acc;
    }, 0);
  }
}

export default BowlingGame;