Created
June 17, 2024 13:14
-
-
Save kristobalus/62ba9844febe5d01767cc16eada1236c to your computer and use it in GitHub Desktop.
bowling game score counter
This file contains 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
/** | |
* @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