Skip to content

Instantly share code, notes, and snippets.

@kristobalus
Created June 17, 2024 13:14
Show Gist options
  • Save kristobalus/62ba9844febe5d01767cc16eada1236c to your computer and use it in GitHub Desktop.
Save kristobalus/62ba9844febe5d01767cc16eada1236c to your computer and use it in GitHub Desktop.
bowling game score counter
/**
* @param {string} game
*/
function getScore(game) {
const records = game.split(" ")
const frames = []
// initial parsing
for(let i = 0; i < records.length - 1; i++) {
const item = records[i]
const frame = {
scores: []
}
if ( item.startsWith("X") ) {
frame.strike = true
frame.scores = [ 10 ]
}
if ( item.includes("/") ) {
frame.spare = true
const [ try1, try2 ] = item.split("")
const first = parseInt(try1, 10)
frame.scores = [ first, 10 - first ]
}
if ( item.includes("-") ) {
frame.miss = true
frame.scores = [ parseInt(item.replace(/[^\d]+/g, ''), 10) ]
}
frames.push(frame)
}
function getLastFrame(records) {
const item = records[records.length - 1]
const frame = {
scores: []
}
const [try1, try2, try3] = item.split("")
if ( try1 == "X" ) {
frame.strike = true
}
else if ( try2 == "/" ) {
frame.spare = true
} else {
frame.miss = true
}
if (frame.miss) {
let first = 0
let second = 0
if ( try1 == "-" ) {
first = 0
}
else {
first = parseInt(try1, 10)
}
if( try2 == "-" ) {
second = 0
} else {
second = parseInt(try2, 10)
}
frame.scores.push(first)
frame.scores.push(second)
}
if (frame.spare) {
const first = parseInt(try1, 10)
const second = 10 - first
frame.scores.push(first)
frame.scores.push(second)
if (try3 == "-") {
// nothing
}
else if ( try3 == "X" ){
frame.scores.push(10)
}
else {
frame.scores.push(parseInt(try3, 10))
}
}
if (frame.strike) {
let first = 10
let second = 0
let third = 0
if ( try2 == "X" ) {
second = 10
}
else if ( try2 == "/" ) {
// impossible
}
else if ( try2 == "-" ) {
second = 0
}
else {
second = parseInt(try2, 10)
}
if ( try3 == "X" ) {
third = 10
}
else if ( try3 == "/" ){
third = 10 - second
}
else if ( try3 == "-" ) {
third = 0
}
else {
// digit
third = parseInt(try3, 10)
}
frame.scores.push(first)
frame.scores.push(second)
frame.scores.push(third)
}
return frame
}
frames.push(getLastFrame(records))
for(let i = 0; i < frames.length; i++) {
const frame = frames[i]
frame.total = frame.scores.reduce((acc, score) => acc + score, 0)
if (frame.spare) {
const next = i < frames.length - 1 ? frames[i + 1].scores : [ 0 ]
const scores = [ ...next ]
frame.total = frame.total + scores[0]
}
if (frame.strike) {
const next1 = i < frames.length - 1 ? frames[i + 1].scores : [ 0 ]
const next2 = i < frames.length - 2 ? frames[i + 2].scores : [ 0 ]
const scores = [ ...next1, ...next2 ]
frame.total = frame.total + scores[0] + scores[1]
}
}
return frames.reduce((acc, frame) => acc + frame.total, 0)
}
const games = [
["9- 9- 9- 9- 9- 9- 9- 9- 9- 9-", 90], // (20 rolls: 10 pairs of 9 and Miss) = 10 frames * 9 points = 90
["6/ 6/ 6/ 6/ 6/ 6/ 6/ 6/ 6/ 6/6", 160], // (21 rolls: 10 pairs of 6 and Spare, with a final 6) = 10 frames * 16 points = 160
["X X X X X X X X X XXX", 300], // (12 rolls: 12 Strikes) = 10 frames * 30 points = 300
]
const [ records, total ] = games[1]
console.log(getScore(records))
games.forEach(g => console.log(`${g[0]}\t${getScore(g[0])}\t${g[1]}`))
// Правила игры
// Игра состоит из 10 фреймов, в каждом фрейме у игрока есть два броска чтобы сбить все 10 кеглей
// Если в десятом фрейме удаётся Spare ("/"), игрок может сделать ещё один бросок
// Если в десятом фрейме первым броском выбивается Strike ("X"), игрок может сделать ещё два броска в этом же фрейме
// Правила подсчёта
// За Miss ("-") 0 очков
// За Spare ("/") дают 10 очков, плюс число кеглей, сбитых при следующем броске
// За Strike ("X") засчитывают 10 очков, плюс число кеглей, сбитых за следующие 2 броска (3 страйка подряд, фрейм = 30 очков)
// Очки за дополнительный бросок/броски в десятом фрейме учитываются только в предыдущих бросках (Strike/Spare)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment