-
Notifications
You must be signed in to change notification settings - Fork 0
/
04-1-solution.js
144 lines (122 loc) · 3.77 KB
/
04-1-solution.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
const NUMBERS_MATCHER = /\d+/g
const NUMBER_MAPPER = s => Number(s)
function parseLine(rawRow) {
;/\d+/g
return rawRow.match(NUMBERS_MATCHER).map(NUMBER_MAPPER)
}
/**
* converts the raw board input into a dictionary, mapping the board numbers
* to where they are located on the board grid.
* @param {*} data the raw board, eg ['1 2 3 4 5', '6 7 8 9 10', ...]
* @returns the number => grid location map, the visual board, and an array of the numbers
*/
function parse(data) {
const output = {}
const visualBoard = []
const numbers = []
data.forEach((rawRowData, rowIndex) => {
const row = parseLine(rawRowData)
visualBoard.push([...row])
row.forEach((item, itemIndex) => {
numbers.push(item)
output[item] = {
row: rowIndex,
column: itemIndex,
}
})
})
return [output, visualBoard, numbers]
}
/**
* a class representing a standard 5x5 bingo board
*/
class Board {
constructor(data, debug = false) {
const [parsedData, rawBoard, numbers] = parse(data)
this.debug = debug
this.data = parsedData
this.visual = rawBoard
this.numbers = numbers
this.rows = [0, 0, 0, 0, 0]
this.columns = [0, 0, 0, 0, 0]
this.called = []
}
has(number) {
return this.data[number]
}
mark(number) {
const rowToUpdate = this.data[number].row
this.rows[rowToUpdate] += 1
const columnToUpdate = this.data[number].column
this.columns[columnToUpdate] += 1
this.visual[rowToUpdate][columnToUpdate] = 'X'
if (this.rows[rowToUpdate] === 5 || this.columns[columnToUpdate] === 5) {
throw new Error(`BINGO when calling ${number}`)
}
}
call(number) {
if (!this.has(number)) return
this.called.push(number)
this.mark(number)
}
score(initial) {
let remainingSum = 0
if (this.debug) console.log('scoring board that won at', initial)
if (this.debug) console.log(this.visual)
this.numbers.forEach(num => {
if (!this.called.includes(num)) {
remainingSum += num
if (this.debug) console.log(`adding ${num} to sum. now it's ${remainingSum}`)
}
})
return initial * remainingSum
}
}
function shouldStopPlaying(winners) {
return winners.length
}
function getBestWinningScore(winners) {
return winners[0].winningScore
}
function solution(
rawInput,
debug = false,
BingoBoard = Board,
shouldStopPlayingCb = shouldStopPlaying,
getBestWinningScoreCb = getBestWinningScore
) {
// pull bingo numbers
const bingoNumbers = rawInput[0].split(',').map(NUMBER_MAPPER)
// pull bingo boards
const boards = []
for (let lineNo = 2; lineNo < rawInput.length; lineNo += 6) {
const nextBoardInput = rawInput.slice(lineNo, lineNo + 5)
boards.push(new BingoBoard(nextBoardInput))
}
// play game
let winners = []
for (let step = 0; step < bingoNumbers.length; step++) {
const currentNumber = bingoNumbers[step]
if (debug) console.log('calling', currentNumber)
boards.forEach((board, boardIndex) => {
try {
board.call(currentNumber)
} catch {
let winningScore = board.score(currentNumber)
winners.push({
currentNumber,
boardIndex,
winningScore,
})
}
})
if (shouldStopPlayingCb(winners)) break
}
if (debug) {
winners.forEach(w => {
console.log(w)
})
}
return getBestWinningScoreCb(winners)
}
module.exports = { solution, Board, NUMBER_MAPPER }