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

Chayada Sansiriwong_Bowling Scorecard in JS #1610

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
99 changes: 15 additions & 84 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,22 @@
# Bowling Scorecard Challenge-JavaScript

Bowling Challenge
=================
## Purpose of the project
It was design a bowling scorecard in JavaScript(JS). The focus was not on creating an interactive game, but a utility for calculating and tracking scores based on user inputs. The task aimed to solidify our understanding of JS, Objective-Oriented Programming(OOP), and Test-Driven Development(TDD).

* 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**
* You must submit a pull request to this repo with your code by 9am Monday week
## What we aimed to learn
Through this project, we aimed to become proficient in JS, as versatile programming language frequently used in web development. We focused on writing behavior-driven tests(BDD) with Jest, utilizing a TDD approach. We also aimed to develop a deeper understanding of OOP principles by dividing our code into separate classes with distinct responsibilities.

## The Task
## What we did
We began by planning our code structure and identifying key components. Utilizing TDD, we wrote tests before implementing functionality, ensuring each class and method behaved as expected. Our project consists of separate classes for handling frames, scorecards, and user interfaces, enhancing modularity and readability.

**THIS IS NOT A BOWLING GAME, IT IS A BOWLING SCORECARD. DO NOT GENERATE RANDOM ROLLS. AN ACTUAL USER INTERFACE IS OPTIONAL**
## What we learned
We learned the nuances of JS syntax and conventions, and gained experience in applying TDD to JS projects. We deepened our understanding of the object-oriented paradigm in JS, focusing on encapsulation and hte Single Responsibility Principle. We also learned to handle the complexity of hte bowling score rules, particularly the bonus scoring system.

Count and sum the scores of a bowling game for one player (in JavaScript).
## Further improvements
These are opportunities for improvements, such as handling a wider variety of user inputs and edge cases. We could extend the application into a full-fledged bowling game simulation. Additionally, adding support for multiple players could be a valuable feature.

A bowling game consists of 10 frames in which the player tries to knock down the 10 pins. In every frame the player can roll one or two times. The actual number depends on strikes and spares. The score of a frame is the number of knocked down pins plus bonuses for strikes and spares. After every frame the 10 pins are reset.
## Issues and learning points
During the development, we found tat managing the bonus scoring system was a complex task. It underscored the importance of thorough planning and precise coding. we learned the value of TDD in helping us isolate and address issues quickly and effectively.

Start by looking in detail at the rules and the example of scoring for a complete game given below.

An example of how your code might be used could be:

```javaScript
let scorecard = new Scorecard()
scorecard.calculateScore() // returns 0
scorecard.addFrame(2, 5)
scorecard.addFrame(3, 5)
scorecard.calculateScore() // returns 15
```

But feel free to add other methods if you think they are useful.

As usual please start by

* Forking this repo

* Using test-driven development (if you decide to write a user interface, then make sure you have looked at the chapters on mocking).

* Finally submit a pull request before Monday week at 9am with your solution or partial solution. However much or little amount of code you wrote please please please submit a pull request before Monday week at 9am.

___STRONG HINT, IGNORE AT YOUR PERIL:___ Bowling is a deceptively complex game. Careful thought and thorough diagramming — both before and throughout — will save you literal hours of your life.

### Optional Extras

In any order you like:

* Set up [Travis CI](https://travis-ci.org) to run your tests.
* Add [ESLint](http://eslint.org/) to your codebase and make your code conform.
* Create a UserInterface class, allowing you to run a game from the command line.

You might even want to start with ESLint early on in your work — to help you
learn Javascript conventions as you go along.

## Bowling — how does it work?

### Strikes

The player has a strike if he knocks down all 10 pins with the first roll in a frame. The frame ends immediately (since there are no pins left for a second roll). The bonus for that frame is the number of pins knocked down by the next two rolls. That would be the next frame, unless the player rolls another strike.

### Spares

The player has a spare if the knocks down all 10 pins with the two rolls of a frame. The bonus for that frame is the number of pins knocked down by the next roll (first roll of next frame).

### 10th frame

If the player rolls a strike or spare in the 10th frame they can roll the additional balls for the bonus. But they can never roll more than 3 balls in the 10th frame. The additional rolls only count for the bonus not for the regular frame count.

10, 10, 10 in the 10th frame gives 30 points (10 points for the regular first strike and 20 points for the bonus).
1, 9, 10 in the 10th frame gives 20 points (10 points for the regular spare and 10 points for the bonus).

### Gutter Game

A Gutter Game is when the player never hits a pin (20 zero scores).

### Perfect Game

A Perfect Game is when the player rolls 12 strikes (10 regular strikes and 2 strikes for the bonus in the 10th frame). The Perfect Game scores 300 points.

In the image below you can find some score examples.

More about ten pin bowling here: http://en.wikipedia.org/wiki/Ten-pin_bowling

![Ten Pin Score Example](images/example_ten_pin_scoring.png)

## Code Review

In code review we'll be hoping to see:

* All tests passing
* The code is elegant: every class has a clear responsibility, methods are short etc.

Reviewers will potentially be using this [code review rubric](docs/review.md). Note that referring to this rubric in advance may make the challenge somewhat easier. You should be the judge of how much challenge you want.
## Conclusion
The Bowling Scorecard Challenge in JS was a valuable exercise that provided hands-on experience with JS and TDD. It underscored the importance of good design principles and provided an opportunity to apply these principles to a complex problem. We will now check our solution against the challenge requirements and run our test suite to ensure everything works as expected before submitting our work. The experience gained from this challenge will undoubtedly prove invaluable in our future JS development endeavors.
31 changes: 31 additions & 0 deletions lib/frame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//frame.js
//define a frame class, represents individual frames in a game
class Frame {
constructor() {
//game starts with clean rolls and no bonus
this.rolls = [];
this.bonus = 0;
}
//a method for adding a roll to the frame
addRoll(pins) {
this.rolls.push(pins);
}
//a methods that getting the total score by using reduce method to calculate the score of the frame
getScore() {
return this.rolls.reduce((a, b) => a + b, 0) + this.bonus;
}
//a method that checks if frame was a strike or not, first roll was 10
isStrike() {
return this.rolls[0] === 10;
}
//a method to check if frame was a spare or not,first and second rolls add up to 10 and not strike
isSpare() {
return this.rolls[0] + this.rolls[1] === 10 && !this.isStrike();
}
//a method for setting the bonus for the frame, incase of a strike or a spare
setBonus(bonus) {
this.bonus = bonus;
}
}

module.exports = Frame;
31 changes: 31 additions & 0 deletions lib/scorecard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// scorecard.js

const Frame = require('./frame');
//define a class to handle the scorecard of the game
class Scorecard {
constructor() {
//start a card with a clean frames
this.frames = [];
}
//a method for adding a frame to the scorecard
addFrame(firstRoll, secondRoll) {
let frame = new Frame();
//adds rolls to the frame
frame.addRoll(firstRoll);
frame.addRoll(secondRoll);
//adds bonus to the frame,if the frame is strike, spare
if(this.frames.length > 0 && (this.frames[this.frames.length - 1].isStrike() || this.frames[this.frames.length - 1].isSpare())) {
this.frames[this.frames.length - 1].setBonus(firstRoll);
}
//adds the frame to the scorecard
this.frames.push(frame);
}
//calculate the score with this method
calculateScore() {
//use reduce function to add the score up
return this.frames.reduce((total, frame) => total + frame.getScore(), 0);
}

}

module.exports = Scorecard;
19 changes: 19 additions & 0 deletions lib/userInterface.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// UserInterface.js

const Scorecard = require('./scorecard');

class UserInterface {
constructor() {
this.scorecard = new Scorecard();
}

addFrame(firstRoll, secondRoll) {
this.scorecard.addFrame(firstRoll, secondRoll);
}

viewScore() {
return this.scorecard.calculateScore();
}
}

module.exports = UserInterface;
38 changes: 38 additions & 0 deletions test/frame.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// frame.test.js

const Frame = require('./frame');

describe('Frame', () => {
//declare the variable frame which we will use in the tests
let frame;

beforeEach(() => {
//before each test, create a new frame
frame = new Frame();
});
//test the addRoll method
test('adds a roll to the frame', () => {
frame.addRoll(5);
expect(frame.getScore()).toBe(5);
});
//test the isStrike method
test('identifies a strike', () => {
frame.addRoll(10);
expect(frame.isStrike()).toBe(true);
});
//test the isSpare method
test('identifies a spare', () => {
frame.addRoll(5);
frame.addRoll(5);
expect(frame.isSpare()).toBe(true);
});
//test the getScore method with bonus
test('calculates score with bonus', () => {
//adds a roll of 5 pins and add anther roll of 5 pins
frame.addRoll(5);
frame.addRoll(5);
//sets a bonus of 10 points
frame.setBonus(10);
expect(frame.getScore()).toBe(20);
});
});
31 changes: 31 additions & 0 deletions test/scorecard.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//scorecard.test.js

const Scorecard = require('./scorecard');

describe('Scorecard', () => {
//declare the variable scorecard that we'll use in the tests
let scorecard;

beforeEach(() => {
//before each test, create a new scorecard
scorecard = new Scorecard();
});
//test the addFrame method
test('starts with a score of 0', () => {
expect(scorecard.calculateScore()).toBe(0);
});
//test the calculateScore method
test('adds a frame to the scorecard', () => {
scorecard.addFrame(2, 5);
expect(scorecard.calculateScore()).toBe(7);
});
//test the bonus for strikes and spares
test('calculates the score for multiple frame', () => {
scorecard.addFrame(2, 5);
scorecard.addFrame(3, 5);
expect(scorecard.calculateScore()).toBe(15);
});
//later adds test for the perfect game, 10th frame etc.,
});

module.exports = Scorecard;
21 changes: 21 additions & 0 deletions test/userInterface.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// userInterface.test.js

const UserInterface = require('./userInterface');

let userInterface;

beforeEach(() => {
userInterface = new UserInterface();
});

test('adds a frame to the scorecard', () => {
userInterface.addFrame(3, 5);
expect(userInterface.viewScore()).toBe(8);
});

test('calculates the total score', () => {
userInterface.addFrame(5, 5);
userInterface.addFrame(3, 4);
//total score = 20
expect(userInterface.viewScore()).toBe(20);
});