Skip to content

Instantly share code, notes, and snippets.

@kasparasg
Created August 30, 2019 19:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kasparasg/2e80b9d2b131d2d361a4048ed0aaf5a4 to your computer and use it in GitHub Desktop.
Save kasparasg/2e80b9d2b131d2d361a4048ed0aaf5a4 to your computer and use it in GitHub Desktop.
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)
})
})
//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