Bowling Challenge Solution

9-pin bowls image

For my YouTube channel, I recorded myself solving the Bowling Challenge. This challenge isn't entirely new to me, I've solved it once before as part of the hiring process for an employer. That was several years ago however, and while I remembered that this was an entertaining challenge, I remembered little about how I'd solved it.

In the video, I make several mistakes, and several poor design decisions, which I then catch and reverse out of. While I was a little tired at the time of recording this, having worked a full work day already, I do not make excuses for these mistakes. Instead, I am pleased that they happened and that I left them in the video. The point being, I'm not trying to be "perfect", but rather to share my thought process as I work the problem.

The code (which may be found below) is not perfect either, nor is it actually what I might submit to an interviewer. When I accomplished this task previously in order to be hired, I took some extra time to review my code before submitting it. This however is not quite the point. If I were challenged to perform this task in a live situation, before a potential employer, I believe that the process of making and correcting mistakes is more valuable than would be acing the task on a first try. It's a fact of development that we make poor design decisions and mistakes, and then reverse them out. What matters to most interviewers is that you do indeed recognize and self correct those decisions.

I doubt that this challenge ever would be used as a live challenge, because even if you were to perfect it on a first try, it ought to take 30 minutes or more for most. That said, I'm sure there are some interviewers that would be so brutal :-) When attending an in-person aptitude test, of course, arrive well rested and ready to present at your best.

Link to the challenge write up: https://codingdojo.org/kata/Bowling/
Warts and all, here's my effort... ( Source below )


Best viewed full-screen 1080p.
program bowling;
{$mode delphiunicode}{$H+}
uses
  SysUtils
;

type
  TFrame = record
    RemainingRolls: uint8; // number of rolls remaining, until frame complete.
    Score: uint8;
  end;

const
  cMaxFramesPerGame = 20;

var
  Score: uint32;
  PrevScore: uint8;
  SecondRoll: boolean;
  FrameIndex: nativeuint;
  Frames: array [ 0 .. pred( cMaxFramesPerGame ) ] of TFrame;

procedure UpdateScore( const Value: uint8 );
var
  lctl: nativeuint;
begin
  PrevScore := Value;
  if FrameIndex = 0 then exit;
  for lctl := FrameIndex downto 0 do begin
    if Frames[ lctl ].RemainingRolls = 0 then continue;
    Frames[ lctl ].Score := Frames[ lctl ].Score + Value;
    dec( Frames[ lctl ].RemainingRolls );
    if Frames[ lctl ].RemainingRolls = 0 then begin
      Score := Score + Frames[ lctl ].Score;
    end;
  end;
end;

function CharToScore( const CH: char ): uint8;
begin
  Result := ord( CH ) - $30; // 0x30 = '0' character in ascii.
end;

procedure AddFrame( const Score: uint8; const RemainingRolls: uint8 );
var
  NewFrameIdx: nativeuint;
begin
  NewFrameIdx := succ( FrameIndex );
  if NewFrameIdx > pred( Length( Frames ) ) then begin
    Writeln('Is this some special extended game?!');
    exit;
  end;
  Frames[ NewFrameIdx ].Score := Score;
  Frames[ NewFrameIdx ].RemainingRolls := RemainingRolls;
  FrameIndex := NewFrameIdx;
end;

procedure ApplyRoll( const CH: Char );
var
  v: uint8;
begin
  case CH of

    '-' : begin
      Score := Score + PrevScore;
      UpdateScore( 0 );
      SecondRoll := not SecondRoll;
    end;

    '/' : begin // spare
      UpdateScore( 10 - PrevScore );
      AddFrame( 10, 1 );
      SecondRoll := False;
    end;

    'X' : begin // strike
      UpdateScore( 10 );
      AddFrame( 10, 2 );
    end;

    '0','1','2','3','4',
    '5','6','7','8','9' : begin
      v := CharToScore( CH );
      if SecondRoll then begin
        Score := Score + v + PrevScore;
      end;
      SecondRoll := not SecondRoll;
      UpdateScore( V );
    end;

    else exit;

  end;
end;

function ReadInput: boolean;
var
  strData: string;
  CH: char;
begin
  Write( ': ' );
  Readln( strData );
  strData := strData.Trim;
  for CH in strData do begin
    ApplyRoll( CH );
  end;
  if Length( strData ) < 1 then exit( true );
  CH := strData[ Length( strData ) ];
  Result := CH <> '~';
end;

begin
  Score := 0;
  FrameIndex := 0;
  PrevScore := 0;
  SecondRoll := False;
  FillChar( Frames[ 0 ], sizeof( TFrame ) * cMaxFramesPerGame, 0 );
  repeat
    Writeln( 'Current Score : ', Score );
  until not ReadInput;
end.