Created
August 30, 2019 19:01
-
-
Save kasparasg/2e80b9d2b131d2d361a4048ed0aaf5a4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import BallClock from './BallClock' | |
describe('BallClock Test', () => { | |
let ballClock | |
beforeEach(() => { | |
ballClock = new BallClock() | |
}) | |
it('should throw error when input number of balls is not between 27 and 127', () => { | |
expect(() => ballClock.init(26)).toThrowError() | |
expect(() => ballClock.init(128)).toThrowError() | |
expect(() => ballClock.init(26, 325)).toThrowError() | |
expect(() => ballClock.init(128, 325)).toThrowError() | |
}) | |
it('should return 15 days and 45 days respectively on mode1', () => { | |
expect(ballClock.init(30)).toBe('30 balls cycle after 15 days.') | |
expect(ballClock.init(45)).toBe('45 balls cycle after 378 days.') | |
}) | |
it('should return correct states of Min, FiveMin, Hour, and Main on mode2 in JSON format', () => { | |
const expectedStr = `{"Min":[],"FiveMin":[22,13,25,3,7],"Hour":[6,12,17,4,15],"Main":[11,5,26,18,2,30,19,8,24,10,29,20,16,21,28,1,23,14,27,9]}` | |
expect(ballClock.init(30, 325)).toBe(expectedStr) | |
}) | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//type constraints for return values for mode 2 | |
interface ModeTwoReturn { | |
Min: Array<number> | |
FiveMin: Array<number> | |
Hour: Array<number> | |
Main: Array<number> | |
} | |
class BallClock { | |
Min: Array<number> | |
FiveMin: Array<number> | |
Hour: Array<number> | |
Main: Array<number> | |
private mode: 1 | 2 | |
private count: number | |
private length: number | |
private targetMin: number | |
constructor() { | |
this.Main = [] | |
this.Min = [] | |
this.FiveMin = [] | |
this.Hour = [] | |
this.mode = 1 | |
this.count = 0 | |
this.length = 0 | |
this.targetMin = 0 | |
} | |
//generate Main track | |
private makeMain(mainTrackLength: number): Array<number> { | |
const mainTrack = [] | |
for (let i = 1; i <= mainTrackLength; i++) { | |
mainTrack.push(i) | |
} | |
return mainTrack | |
} | |
//remove ball from Main track | |
private shiftMain(): number { | |
return this.Main.shift() | |
} | |
//add Main ball to Min track | |
private mainToMin(): void { | |
this.Min.push(this.shiftMain()) | |
} | |
//add first four Min balls to Main track in reverse | |
private minToMain(): void { | |
for (let i = 3; i >= 0; i--) { | |
this.Main.push(this.Min[i]) | |
} | |
} | |
//add fifth Min ball to FiveMin track | |
private minToFiveMin(): void { | |
this.FiveMin.push(this.Min[4]) | |
} | |
//add first eleven FiveMin balls to Main track in reverse | |
private fiveMinToMain(): void { | |
for (let i = 10; i >= 0; i--) { | |
this.Main.push(this.FiveMin[i]) | |
} | |
} | |
//add twelveth's FiveMin ball to Hour track | |
private fiveMinToHour(): void { | |
this.Hour.push(this.FiveMin[11]) | |
} | |
//add eleven Hour balls to Main track in reverse order and twelveth's ball at last | |
private hourToMain(): void { | |
for (let i = 10; i >= 0; i--) { | |
this.Main.push(this.Hour[i]) | |
} | |
this.Main.push(this.Hour[11]) | |
} | |
//every minute add Main ball to Min track and count minutes passed only on mode2 | |
private everyMin(): void { | |
this.mainToMin() | |
if (this.mode === 2) { | |
this.count += 1 | |
} | |
} | |
//every five minute add Min balls to Main track and FiveMin track and empty Min track | |
private everyFiveMin(): void { | |
this.minToMain() | |
this.minToFiveMin() | |
this.Min.length = 0 | |
} | |
//every hour add FiveMin balls to Main track and Hour track and empty FiveMin track | |
private everyHour(): void { | |
this.fiveMinToMain() | |
this.fiveMinToHour() | |
this.FiveMin.length = 0 | |
} | |
//every twelve hour add Hour balls to Main track and empty Hour track | |
//count the number of half-day only on mode 1 | |
private everyTwelveHour(): void { | |
this.hourToMain() | |
this.Hour.length = 0 | |
if (this.mode === 1) { | |
this.count += 0.5 | |
} | |
} | |
//on mode 1 | |
//check if Main track has balls in the same state as the beginning and, thus, one cycle has completed | |
private isCycle(): boolean { | |
if (this.Main.length === this.length) { | |
for (let i = 0; i < this.Main.length; i++) { | |
if (this.Main[i] !== i + 1) return false | |
} | |
return true | |
} else { | |
return false | |
} | |
} | |
//on mode 2 | |
//check if specified number of minutes have passed to determine whether clock should stop | |
private isTargetMin(): boolean { | |
return this.count === this.targetMin | |
} | |
//use isCycle() on mode1 and use isTargetMin() on mode2 to determine when clock should stop running | |
private check(): boolean { | |
return this.mode === 1 ? this.isCycle() : this.isTargetMin() | |
} | |
//keep running the clock until this.check method returns true | |
private runClock(): void { | |
let shouldStop = false | |
while (!shouldStop) { | |
this.everyMin() | |
if (this.Min.length === 5) { | |
this.everyFiveMin() | |
} | |
if (this.FiveMin.length === 12) { | |
this.everyHour() | |
} | |
if (this.Hour.length === 12) { | |
this.everyTwelveHour() | |
} | |
shouldStop = this.check() | |
} | |
} | |
//determine what BallClock should return based on mode | |
private toReturn(): string | ModeTwoReturn { | |
return this.mode === 1 | |
? `${this.length} balls cycle after ${this.count} days.` | |
: JSON.stringify({ | |
Min: this.Min, | |
FiveMin: this.FiveMin, | |
Hour: this.Hour, | |
Main: this.Main, | |
}) | |
} | |
//initialize the clock to start in a clean slate with empty tracks | |
//update this.targetMin on mode 2 | |
private initialize(mainLength: number, targetMin?: number): void { | |
this.mode = !targetMin ? 1 : 2 | |
this.targetMin = !targetMin ? this.targetMin : targetMin | |
this.Main = this.makeMain(mainLength) | |
this.Min = [] | |
this.FiveMin = [] | |
this.Hour = [] | |
this.length = mainLength | |
this.count = 0 | |
} | |
//throw error if length is out of required range | |
private lengthCheck(mainLength: number): boolean | void { | |
if (mainLength > 26 && mainLength < 127) { | |
return true | |
} else { | |
throw new RangeError( | |
'The Number Of Balls Should Be More Than 27 And Less Than 127', | |
) | |
} | |
} | |
//initialize the clock to run in mode 1 or mode 2 depending on the presence of targetMin | |
init(mainLength: number, targetMin?: number): string | ModeTwoReturn { | |
this.lengthCheck(mainLength) | |
if (!targetMin) { | |
this.initialize(mainLength) | |
} else { | |
this.initialize(mainLength, targetMin) | |
} | |
this.runClock() | |
return this.toReturn() | |
} | |
} | |
export default BallClock | |
const clock = new BallClock() | |
console.log(clock.init(30)) //mode1 | |
console.log(clock.init(30, 325)) //mode2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment