Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript bowling challenge - Louis Claremont #1621

Open
wants to merge 58 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
936db3f
create frame class
May 26, 2023
c11c288
ESLint installed
May 26, 2023
147ce40
passing find score for a simple frame
May 26, 2023
eebe596
passing test for checking if frame is a strike
May 26, 2023
529943d
passing test for null score when strike or spare thrown
May 26, 2023
8e28b25
pass test for new scorecard instance
May 26, 2023
94aa3d4
pass test for adding one frame to frames array
May 26, 2023
357d819
passing test for two frames added to scorecard
May 26, 2023
3a2933a
cleaned up test for frame rolls
May 26, 2023
5ccc017
passing tests for calculateScore
May 26, 2023
db5679d
passing test for spare on first frame
May 26, 2023
83f32fa
passing test for one strike added
May 26, 2023
e4d9a05
changed score to be an instance variable and frameScore to getFrameScore
May 27, 2023
0c5e1bb
passing test for updating the spare bonus based on a next frame
May 27, 2023
62e941b
passing test for updating spare score when next roll is strike
May 27, 2023
ea1ff86
scores a strike when next frame is zero
May 27, 2023
e8baf15
passing tests for strike followed by simple frame and spare
May 27, 2023
be869de
passing test for strike followed by a strike
May 27, 2023
ffc9afb
passing test for strike followed by strike and simple frame
May 27, 2023
2c753a6
passing tests for strike followed by two strikes
May 27, 2023
d8fc1f6
passing tests for final frame
May 27, 2023
df0bb2a
passing test for two simple frames added
May 27, 2023
803807b
updating scores for when spare then simple frame
May 27, 2023
a9fc08a
calculates correct score after two frames thrown
May 27, 2023
7d3ac74
calculates correct score after 3 frames thrown
May 27, 2023
5cf68bb
refactored methods in scorecard class
May 27, 2023
2ff2646
passing test for 3 spares and zero frame
May 27, 2023
7f4ee49
passing test for first frame a strike
May 27, 2023
e70418a
passing test fot one strike and one simple frame
May 27, 2023
a816cce
passing test for two strikes
May 27, 2023
a9c7b58
passing test for two strikes and a simple frame
May 27, 2023
76f4d6e
passing test for spare and strikes
May 27, 2023
fcc9776
passing test for final frame a spare
May 27, 2023
40773c4
passing test for final frame a strike
May 27, 2023
71c27f1
passing test for perfect game
May 27, 2023
2836ea6
passing first display of scorecard test
May 27, 2023
9b18e9d
passing scorecard display for three strikes
May 28, 2023
0c8e1d0
scorecard shows final frame when it is a strike
May 28, 2023
22555f0
express project setup
May 28, 2023
2f64de3
Merge pull request #1 from lplclaremont/express
lplclaremont May 28, 2023
64531fc
refactored post/frame route
May 28, 2023
bc055f3
refacroting scorewithStrikeBonus method
May 28, 2023
d715d7f
further refactoring the scoreWithStrikeBonus method
May 28, 2023
9dd555a
refactoring reduce functions for summing frames
May 28, 2023
73fbb45
refactoring the updateScores method
May 28, 2023
50e057c
updated show method to return JSON object
May 28, 2023
9c6d2bb
change format of the JSON return for show() method
May 28, 2023
39b1651
changing format of the json scorecard
May 28, 2023
e4e49bd
passing all tests now
May 28, 2023
c62c713
updated README
May 28, 2023
95ec948
updating format of readme
May 28, 2023
08b3970
adding to readme
May 28, 2023
ca49f06
removing colon
May 28, 2023
5f7c31e
updating readme again
May 28, 2023
01b0c8e
updating readme noteee
May 28, 2023
cc618dc
adding to readme the language
May 28, 2023
9155617
moved backend into api directory
Jun 8, 2023
d42263b
removing the console logs in scorecard.js
Jun 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@
Bowling Challenge
=================

# Overview
In this challenge I have created a javascript program which will score a bowling game (10 frames) based on the rolls thrown in each frame, which will be inputted by the user.
The logic for this proved very difficult - since when a spare or a strike is thrown, the total score for that frame is unknown until the next roll(s) are complete.
I have a frame class which handles the behaviour of a frame. This includes checking whether the frame is a spare or strike, finding the number of pins knocked down and updating the spare/strike frame's score based on the following frame (or following two frames if a strike is followed by another strike).
Then I created a scorecard class which allows a user to add a frame and calculate the total score up to a given point. This is useful since on the scorecard, if a spare is thrown, we do not want to update the current score straight away since we need to wait for the following frame. Hence, we would like to know the score up to the previous frame (to retrospectively show the running total at that point) in order to correctly score the frames one by one.

**NOTE** --
In my tests, I refer to a frame which is not a spare or a strike as a 'simple frame'.

## Further
I have added a server.js file which runs an express application, so that you can view the scorecard. I have added a POST /frame route so that you are able to add a frame through body parameters. This can be built upon to create a UI for users to add frames and view their scorecard.


## Instruction

* Feel free to use google, your notes, books, etc. but work on your own
* If you refer to the solution of another coach or student, please put a link to that in your README
* If you have a partial solution, **still check in a partial solution**
Expand Down
15 changes: 15 additions & 0 deletions api/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": "eslint:recommended",
"overrides": [
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
}
}
28 changes: 28 additions & 0 deletions api/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const express = require('express');
const app = express();
const port = 3000;

const Scorecard = require('./src/scorecard.js');
const scorecard = new Scorecard()

app.use(express.json());

app.get('/', (req, res) => {
res.send('Bowling scorecard')
});

app.get('/scorecard', (req, res) => {
res.send(scorecard.show())
})

app.post('/frame', (req, res) => {
const { roll1, roll2, roll3 } = req.body;
const rolls = [roll1, roll2, roll3].filter(roll => roll !== undefined);

scorecard.addFrame(...rolls);

res.send('Frame added')
})

console.log(`Server listening on localhost:${port}`);
app.listen(port);
42 changes: 42 additions & 0 deletions api/src/frame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class Frame {

constructor(rolls) {
this.rolls = rolls,
this.score = null
}

getFrameScore() {
if (this.spare() || this.strike()) {
return this.score;
} else {
return this.#pinsDown();
}
}

spare() {
return this.#pinsDown() === 10 && this.rolls.length === 2;
}

strike() {
return this.#pinsDown() === 10 && this.rolls.length === 1;
}

scoreWithSpareBonus(frame) {
this.score = 10 + frame.rolls[0];
}

scoreWithStrikeBonus(frame1, frame2) {
if (frame1.strike() && frame2 !== undefined) {
this.score = 20 + frame2.rolls[0];
} else if (!frame1.strike()) {
this.score = 10 + frame1.rolls[0] + frame1.rolls[1];
}
}

#pinsDown() {
return this.rolls.reduce(
(sum, pins) => sum += pins, 0)
}
}

module.exports = Frame;
63 changes: 63 additions & 0 deletions api/src/scorecard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const Frame = require('./frame');

class Scorecard {
constructor() {
this.frames = [];
}

addFrame() {
const rolls = Array.from(arguments);
const frame = new Frame(rolls);
this.frames.push(frame);
this.#updateScores()
}

currentScore() {
return this.calculateScoreUpTo(this.frames.length);
}

calculateScoreUpTo(i) {
return this.frames.slice(0, i + 1)
.map((frame) => frame.getFrameScore())
.reduce((sum, num) => sum += num, 0)
}

show() {
let scoresArray = []
const framesLength = this.frames.length;

for(let i = 0 ; i < framesLength ; i ++) {
const currentFrame = this.frames[i]
const frameData = {
"rolls": currentFrame.rolls,
"score": this.calculateScoreUpTo(i)
}

scoresArray.push(frameData);
}

return {"scorecard": scoresArray}
}

#updateScores() {
for(let i = 0 ; i < this.frames.length - 1 ; i++) {
const currentFrame = this.frames[i]
const frameScore = currentFrame.getFrameScore()

if (frameScore !==null) {
continue
}

const nextFrame = this.frames[i + 1]
const frameAfterNext = this.frames[i + 2]

if (currentFrame.spare()) {
currentFrame.scoreWithSpareBonus(nextFrame);
} else if (currentFrame.strike()) {
currentFrame.scoreWithStrikeBonus(nextFrame, frameAfterNext)
}
}
}
}

module.exports = Scorecard;
148 changes: 148 additions & 0 deletions api/tests/frame.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
const Frame = require('../src/frame');

describe('Frame', () => {
it('constructs a zero frame', () => {
frame = new Frame([0, 0]);

expect(frame.rolls).toEqual([0, 0]);
expect(frame.getFrameScore()).toEqual(0);
})

it('finds score of a simple frame', () => {
frame = new Frame([1, 2]);

expect(frame.rolls).toEqual([1, 2]);
expect(frame.getFrameScore()).toEqual(3);
})

it('recognises a spare', () => {
frame = new Frame([4, 6]);
frame2 = new Frame([4, 5]);

expect(frame.spare()).toEqual(true);
expect(frame2.spare()).toEqual(false);
})

it('recognises a strike', () => {
frame = new Frame([10]);

expect(frame.spare()).toEqual(false);
expect(frame.strike()).toEqual(true);
})

it('scores a spare or strike as null', () => {
frame = new Frame([10]);
frame2 = new Frame([8, 2]);

expect(frame.getFrameScore()).toEqual(null);
expect(frame2.getFrameScore()).toEqual(null);
})


describe('scoring a spare frame', () => {
it('updates spare score once next roll is 0', () => {
frame = new Frame([4, 6])
frame2 = new Frame([0, 0])

frame.scoreWithSpareBonus(frame2)
expect(frame.getFrameScore()).toEqual(10)
})

it('updates spare score once next roll is 4', () => {
frame = new Frame([4, 6])
frame2 = new Frame([4, 0])

expect(frame.getFrameScore()).toEqual(null)
frame.scoreWithSpareBonus(frame2)
expect(frame.getFrameScore()).toEqual(14)
})

it('updates spare score once next roll is strike', () => {
frame = new Frame([4, 6])
frame2 = new Frame([10])

expect(frame.getFrameScore()).toEqual(null)
frame.scoreWithSpareBonus(frame2)
expect(frame.getFrameScore()).toEqual(20)
})
})

describe('scoring a strike frame', () => {
it('updates the score when followed by zero frame', () => {
frame = new Frame([10])
frame2 = new Frame([0, 0])

expect(frame.getFrameScore()).toEqual(null)
frame.scoreWithStrikeBonus(frame2, undefined)
expect(frame.getFrameScore()).toEqual(10)
})

it('updates the score when followed by simple frame', () => {
frame = new Frame([10])
frame2 = new Frame([2, 2])

expect(frame.getFrameScore()).toEqual(null)
frame.scoreWithStrikeBonus(frame2, undefined)
expect(frame.getFrameScore()).toEqual(14)
})

it('updates the score when followed by a spare', () => {
frame = new Frame([10])
frame2 = new Frame([2, 8])

expect(frame.getFrameScore()).toEqual(null)
frame.scoreWithStrikeBonus(frame2, undefined)
expect(frame.getFrameScore()).toEqual(20)
})

it('does not update the score when followed by a strike', () => {
frame = new Frame([10])
frame2 = new Frame([10])

frame.scoreWithStrikeBonus(frame2, undefined)
expect(frame.getFrameScore()).toEqual(null)
})

it('updates the score when followed by a strike then zero', () => {
frame = new Frame([10])
frame2 = new Frame([10])
frame3 = new Frame([0, 5])
frame.scoreWithStrikeBonus(frame2, frame3)

expect(frame.getFrameScore()).toEqual(20)
})

it('updates the score when followed by a strike then simplle frame', () => {
frame = new Frame([10])
frame2 = new Frame([10])
frame3 = new Frame([2, 5])
frame.scoreWithStrikeBonus(frame2, frame3)

expect(frame.getFrameScore()).toEqual(22)
})

it('updates the score when followed by two strikes', () => {
frame = new Frame([10])
frame2 = new Frame([10])
frame3 = new Frame([10])
frame.scoreWithStrikeBonus(frame2, frame3)

expect(frame.getFrameScore()).toEqual(30)
})
})

describe('final frame', () => {
it('scores a spare on final frame', () => {
frame = new Frame([3, 7, 4])
frame2 = new Frame([5, 5, 0])

expect(frame.getFrameScore()).toEqual(14)
expect(frame2.getFrameScore()).toEqual(10)
})

it('scores a strike on final frame', () => {
frame = new Frame([10, 5, 5])
expect(frame.getFrameScore()).toEqual(20)
})
})
})
Loading