From 7f8a13f2706ed58424e8600cc86f18d762d493b5 Mon Sep 17 00:00:00 2001 From: Anthony McLin Date: Mon, 13 Dec 2021 22:34:52 -0800 Subject: [PATCH] feat(2021-day-04): find the first board with a bingo --- 2021/day-04/bingo.js | 76 +++++ 2021/day-04/bingo.test.js | 133 +++++++++ 2021/day-04/index.js | 3 + 2021/day-04/input.txt | 601 ++++++++++++++++++++++++++++++++++++++ 2021/day-04/solution.js | 74 +++++ index.js | 2 +- 6 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 2021/day-04/bingo.js create mode 100644 2021/day-04/bingo.test.js create mode 100644 2021/day-04/index.js create mode 100644 2021/day-04/input.txt create mode 100644 2021/day-04/solution.js diff --git a/2021/day-04/bingo.js b/2021/day-04/bingo.js new file mode 100644 index 0000000..5f3657c --- /dev/null +++ b/2021/day-04/bingo.js @@ -0,0 +1,76 @@ +const boards = [] + +const callNumber = (called) => { + for (let x = 0; x < boards.length; x++) { + markBoard(x, called) + if (checkWinner(x) === 'winner') { + console.debug(`Board ${x} is the winner`) + return x + } + } +} + +const markBoard = (board, called) => { + for (let x = 0; x < 5; x++) { + for (let y = 0; y < 5; y++) { + if (board[x][y] === called) { + board[x][y] = 'x' + // TODO: speed up break the loop, since only one of a number on each board + // x = 6 + // y = 6 + } + } + } + return board +} + +const checkWinner = (board) => { + // TODO: This can be sped up by doing a check for at least 5 "x" before + // validating horizontal/vertical explicitly. Another speedup would be to + // zig-zag check parse through the array and break/resolve when there + // isn't a match instead of checking all columns then checking all rows + + // Look for a horizontal bingo + for (let y = 0; y < 5; y++) { + if (board[y].filter((val) => val === 'x').length === 5) { + return 'winner' + } + } + + // Look for a vertical bingo + let match = 0 + for (let x = 0; x < 5; x++) { + for (let y = 0; y < 5; y++) { + if (board[y][x] === 'x') { + match++ + + if (match === 5) { + return 'winner' + } + } + } + match = 0 // reset so next row has a clean count + } + + // No bingo + return 'no win' +} + +const scoreBoard = (board) => { + return board.reduce((tally, row) => { + tally += row.reduce((colTally, cell) => { + if (cell !== 'x') { + colTally += cell + } + return colTally + }, 0) + return tally + }, 0) +} + +module.exports = { + callNumber, + scoreBoard, + checkWinner, + markBoard +} diff --git a/2021/day-04/bingo.test.js b/2021/day-04/bingo.test.js new file mode 100644 index 0000000..90cb97e --- /dev/null +++ b/2021/day-04/bingo.test.js @@ -0,0 +1,133 @@ +/* eslint-env mocha */ +const { expect } = require('chai') +const { callNumber, scoreBoard, checkWinner, markBoard } = require('./bingo') +const { parseData } = require('../../2018/inputParser') + +const testData = ` +7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 + +22 13 17 11 0 + 8 2 23 4 24 +21 9 14 16 7 + 6 10 3 18 5 + 1 12 20 15 19 + + 3 15 0 2 22 + 9 18 13 17 5 +19 8 7 25 23 +20 11 10 24 4 +14 21 16 12 6 + +14 21 17 24 4 +10 16 15 9 19 +18 8 23 26 20 +22 11 13 6 5 + 2 0 12 3 7 +` +// Deep copy to ensure we aren't mutating the original data +const data = JSON.parse(JSON.stringify(testData)) + +// split up data +const testDraws = parseData(data.shift()) +const testBoards = [] +for (let x = 0; x < data.length; x = x + 5) { + testBoards.push( + data.slice(x, x + 5).map(parseData) + ) +} + +describe('--- Day 4: Giant Squid ---', () => { + describe('Part 1', () => { + describe('markBoard()', () => { + it('checks a board for a match and marks it', () => { + const board = [ + [1, 2, 3, 4, 5], + [9, 8, 7, 6, 5], + ['x', 'x', 'x', 'x', 'x'], + [3, 6, 9, 1, 0], + [1, 3, 5, 7, 9] + ] + const expected = [ + [1, 2, 3, 4, 'x'], + [9, 8, 7, 6, 'x'], + ['x', 'x', 'x', 'x', 'x'], + [3, 6, 9, 1, 0], + [1, 3, 'x', 7, 9] + ] + expect(markBoard(board, 5)).to.deep.equal(expected) + }) + it.skip('can be used in a loop to find', () => { + // callNumber(7) + // callNumber(4) + // callNumber(9) + // callNumber(5) + // callNumber(11) + // for(var x = 0; x < testBoards) + // expect(boards[0]).to.deep.equal(board0) + // expect(boards[1]).to.deep.equal(board1) + // expect(boards[2]).to.deep.equal(board2) + // callNumber(17) + // callNumber(23) + // callNumber(2) + // callNumber(0) + // callNumber(14) + // callNumber(21) + // expect(boards[0]).to.deep.equal(board0) + // expect(boards[1]).to.deep.equal(board1) + // expect(boards[2]).to.deep.equal(board2) + }) + it.skip('identifies the winner', () => { + expect(callNumber(24)).to.equal(3) + }) + }) + describe('findWinner()', () => { + it.skip('loops through the boards and checks for a winner', () => { + + }) + }) + describe('checkWinner()', () => { + it('checks to see if a board has a horizontal bingo', () => { + const board = [ + [1, 2, 3, 4, 5], + [9, 8, 7, 6, 5], + ['x', 'x', 'x', 'x', 'x'], + [3, 6, 9, 1, 0], + [1, 3, 5, 7, 9] + ] + expect(checkWinner(board)).to.equal('winner') + }) + it('checks to see if a board has a vertical bingo', () => { + const board = [ + [1, 2, 3, 'x', 5], + [9, 8, 7, 'x', 5], + [2, 4, 6, 'x', 8], + [3, 6, 9, 'x', 0], + [1, 3, 5, 'x', 7] + ] + expect(checkWinner(board)).to.equal('winner') + }) + it('identifies a board is not yet a winner', () => { + const board = [ + [1, 'x', 3, 4, 5], + [9, 8, 7, 'x', 5], + ['x', 'x', 3, 7, 11], + [3, 6, 9, 'x', 'x'], + [1, 3, 5, 7, 'x'] + ] + expect(checkWinner(board)).to.equal('no win') + }) + }) + describe('scoreBoard()', () => { + it('gets the sum of the unmarked squares on the board', () => { + const board = [ + ['x', 'x', 'x', 'x', 'x'], + [10, 16, 15, 'x', 19], + [18, 8, 'x', 26, 20], + [22, 'x', 13, 6, 'x'], + ['x', 'x', 12, 3, 'x'] + ] + expect(scoreBoard(board)).to.equal(188) + }) + }) + }) +}) diff --git a/2021/day-04/index.js b/2021/day-04/index.js new file mode 100644 index 0000000..af7e035 --- /dev/null +++ b/2021/day-04/index.js @@ -0,0 +1,3 @@ +// eslint-disable-next-line no-unused-vars +const console = require('../helpers') +require('./solution') diff --git a/2021/day-04/input.txt b/2021/day-04/input.txt new file mode 100644 index 0000000..5bab2b0 --- /dev/null +++ b/2021/day-04/input.txt @@ -0,0 +1,601 @@ +1,76,38,96,62,41,27,33,4,2,94,15,89,25,66,14,30,0,71,21,48,44,87,73,60,50,77,45,29,18,5,99,65,16,93,95,37,3,52,32,46,80,98,63,92,24,35,55,12,81,51,17,70,78,61,91,54,8,72,40,74,68,75,67,39,64,10,53,9,31,6,7,47,42,90,20,19,36,22,43,58,28,79,86,57,49,83,84,97,11,85,26,69,23,59,82,88,34,56,13 + +85 23 65 78 93 +27 53 10 12 26 + 5 34 83 25 6 +56 40 73 29 54 +33 68 41 32 82 + + 8 31 14 70 91 +53 49 86 13 21 +66 28 76 78 93 +39 63 80 43 23 +56 25 60 67 72 + +67 78 36 64 14 +46 16 80 23 94 +22 47 51 65 57 +33 76 21 92 97 +31 95 54 27 20 + + 1 77 86 43 30 +28 88 7 5 60 +66 24 3 57 33 +38 23 59 84 44 +74 47 17 29 85 + +21 50 86 2 70 +85 19 22 93 25 +99 38 74 30 65 +81 0 47 78 63 +34 11 51 88 64 + +45 15 29 81 30 +75 21 88 91 49 +39 20 4 17 78 +10 12 38 11 7 +98 6 65 69 86 + +36 20 31 44 69 +30 65 55 88 64 +74 85 82 61 5 +57 17 90 43 54 +58 83 52 23 7 + +42 16 82 86 76 +60 26 27 59 55 + 7 53 22 78 5 +18 61 10 15 17 +28 46 14 87 77 + +21 43 15 47 61 +24 76 28 3 27 +19 62 69 82 93 +49 29 97 74 41 +92 36 37 99 40 + +31 4 3 62 51 +24 57 78 67 53 +13 5 76 38 55 +79 9 75 98 71 +65 1 39 18 47 + +59 4 38 95 99 +85 68 69 93 43 +83 57 48 42 15 +47 50 80 79 90 +56 87 78 64 25 + +21 37 14 67 95 +88 39 26 38 49 +89 83 54 77 96 +48 86 94 19 20 +43 41 8 74 58 + + 1 36 12 90 91 +63 21 98 82 66 +39 86 7 52 77 +80 81 44 33 58 +78 30 11 51 28 + +81 74 7 33 96 +75 60 87 47 91 +39 73 30 50 13 + 4 41 9 43 77 +34 82 72 48 12 + +93 63 74 25 57 +29 76 9 45 70 +98 77 71 16 41 +47 54 18 14 55 +31 89 67 87 83 + + 8 72 45 93 68 +74 26 69 94 65 +28 9 20 47 41 +46 54 21 56 22 +84 62 18 15 48 + +20 51 81 40 69 +71 10 13 93 75 +44 86 0 95 37 +99 39 76 80 66 +14 64 49 62 27 + +75 7 51 86 79 +43 30 61 39 16 +85 63 90 28 96 +88 78 72 31 73 +98 87 23 19 58 + +20 95 47 97 12 +92 25 68 87 91 +37 10 78 23 63 +74 93 58 39 5 +76 51 48 72 16 + +37 18 32 34 85 +22 31 98 42 19 +29 72 48 76 25 +47 1 21 7 53 +79 82 86 52 78 + +20 16 47 78 92 +88 15 71 67 2 + 5 52 90 70 9 +22 49 28 82 27 + 6 19 61 73 48 + +71 26 7 11 79 +52 30 47 1 31 +17 75 94 91 28 +81 98 23 55 21 +77 15 39 24 16 + + 5 75 44 88 65 +89 45 23 69 19 +41 61 67 52 54 +47 38 57 12 98 +62 70 26 87 53 + +50 4 65 77 25 + 6 21 5 27 92 +39 63 97 75 79 +60 34 87 26 74 +99 24 44 85 2 + +13 64 38 78 21 +74 17 83 57 94 +25 39 69 53 4 +54 33 81 50 76 +42 75 19 77 26 + +63 31 70 19 39 +38 87 15 90 75 +61 98 6 29 86 +78 62 32 11 60 +55 97 13 73 82 + +51 63 68 84 36 +12 33 37 31 8 +18 41 34 74 23 +72 39 85 48 60 +24 19 29 88 0 + +46 51 17 23 13 +20 93 97 99 81 +57 47 33 84 44 +28 96 2 43 56 +68 36 62 15 5 + +81 99 5 30 10 +38 62 57 8 37 + 7 86 98 3 54 +46 82 96 15 72 +83 1 75 25 50 + +47 57 11 61 27 +53 10 31 91 98 +76 85 55 38 23 + 6 81 67 71 70 +35 29 17 50 56 + +24 65 15 1 89 +45 60 97 23 14 +84 56 58 5 54 + 3 72 51 46 79 +67 70 78 34 77 + +38 11 54 23 2 +33 14 10 96 63 +43 5 36 20 30 +70 53 66 71 9 +91 90 21 7 88 + +94 44 4 86 26 +39 70 54 50 30 +55 40 12 72 71 +68 7 66 47 91 +31 24 13 1 96 + +79 14 40 87 68 +16 32 53 46 98 +38 95 21 89 69 +62 60 19 81 33 +70 52 28 83 0 + +62 42 38 48 64 +61 79 78 97 98 +89 7 3 29 68 +92 76 14 67 1 +41 99 72 47 60 + + 5 75 18 42 33 +72 61 36 31 29 +19 58 1 34 94 +54 84 92 99 38 +76 68 79 53 37 + +14 91 37 5 98 +68 29 34 76 43 +75 0 67 33 69 +81 47 58 30 93 +88 92 42 77 54 + +64 24 28 54 53 +72 68 3 73 4 +83 6 59 66 94 +87 80 55 20 16 +13 82 74 31 70 + +63 92 71 0 83 +98 40 50 55 2 +88 5 85 30 23 +10 75 81 58 68 +51 31 14 89 1 + +67 93 94 54 53 +38 71 34 40 24 +31 63 30 99 75 + 4 57 86 19 70 +60 49 87 68 74 + +56 94 79 53 7 +24 12 19 6 99 +82 51 41 46 43 +17 49 52 78 55 +75 48 61 70 87 + +14 55 32 21 31 +88 83 23 44 4 + 1 77 45 90 85 +46 81 51 27 62 +60 24 29 18 0 + +95 92 91 27 26 +22 43 45 64 62 +83 23 25 85 94 +84 53 72 28 20 +75 60 52 18 73 + +95 41 7 21 32 +58 65 16 56 97 +68 25 91 83 24 +66 89 15 55 6 + 2 30 84 10 90 + +58 86 44 19 74 +57 89 17 6 83 +77 35 60 32 13 +97 63 62 28 76 +55 31 11 0 52 + +33 39 59 42 45 +61 50 92 9 79 +15 0 28 5 72 +91 24 21 29 87 +86 76 43 31 93 + +63 11 86 45 85 +96 74 66 93 32 +95 30 99 23 18 +69 97 48 15 1 +42 87 47 83 80 + +93 5 40 64 2 +44 51 15 54 83 +69 77 90 58 11 + 0 48 43 30 55 +25 72 38 73 52 + +89 58 71 68 15 +23 65 9 36 74 +21 29 42 79 98 +55 47 33 39 28 +16 75 91 69 57 + +13 79 12 71 2 +60 94 99 43 82 +84 89 29 91 87 +74 80 25 32 21 +70 14 68 92 11 + +78 1 16 51 87 +58 94 59 15 43 +79 41 50 47 39 +53 37 9 28 72 +34 63 89 35 18 + +31 67 70 42 43 +60 2 89 49 22 +56 17 81 24 74 +20 65 1 96 51 +68 7 0 38 25 + +59 14 29 53 19 + 9 2 11 33 44 +81 6 10 47 58 +20 34 62 55 40 +71 38 69 45 78 + +59 36 70 42 21 + 3 16 49 79 98 +74 25 8 84 19 +61 80 47 65 64 +91 62 52 9 40 + + 1 85 63 7 2 + 0 20 61 26 77 +99 37 74 42 76 +25 94 19 78 60 +79 72 95 22 11 + +51 21 79 76 32 +55 23 69 19 61 +71 54 94 47 92 + 5 64 6 68 16 +91 81 9 99 30 + +61 69 82 86 68 +66 81 28 38 36 +26 29 31 11 8 +72 51 12 95 63 +18 30 88 17 32 + +34 8 14 42 67 +66 79 65 20 52 +37 87 74 24 3 +59 54 21 32 89 +31 4 62 76 30 + +11 93 8 92 55 +38 72 99 3 83 +12 75 0 41 46 +17 25 5 39 48 +14 18 86 29 84 + + 6 20 41 51 48 + 5 67 30 24 47 + 3 8 92 22 39 + 4 56 36 31 75 + 2 45 85 81 96 + +47 43 72 22 3 +19 87 53 12 60 +29 40 56 68 18 +66 97 70 33 39 +85 37 0 90 98 + +61 35 81 84 94 +11 1 58 45 77 + 6 99 67 36 43 + 5 7 0 87 80 +44 78 39 70 20 + +58 34 49 29 75 +17 15 28 23 84 +59 25 92 48 0 +20 81 47 3 71 +68 60 5 22 87 + +90 32 41 39 6 +36 78 67 24 50 +55 72 52 75 44 +87 15 92 31 58 +83 89 68 19 43 + +99 44 53 68 25 +71 67 16 19 36 +35 58 14 86 48 +88 18 61 24 23 +87 9 91 37 15 + +37 5 63 68 28 +41 50 76 99 64 +34 92 78 94 71 +11 96 97 42 58 +33 45 0 93 48 + +33 68 9 12 81 +60 98 28 8 99 +14 17 6 82 15 +57 69 43 38 29 +47 84 76 22 18 + +79 70 92 38 47 +12 82 98 46 0 +76 15 53 59 97 +18 52 49 29 96 +44 64 68 89 24 + +95 14 17 27 42 +55 43 57 29 25 +34 73 86 50 16 +69 37 75 63 39 +78 79 3 4 30 + +27 31 15 92 46 +36 23 72 40 50 +51 99 55 89 21 +12 70 84 63 85 +78 88 77 75 0 + +15 67 40 39 28 + 9 79 22 52 75 +96 65 86 98 14 +97 87 44 84 68 +36 26 89 43 27 + +79 59 48 27 36 +85 92 93 76 24 + 2 25 7 42 90 +23 29 74 35 86 +58 60 31 75 57 + +10 43 83 75 8 +88 12 38 30 9 +60 67 59 76 6 +55 45 74 34 25 +97 49 65 96 69 + +59 86 15 3 19 +89 4 74 61 23 +52 98 8 79 39 +95 17 22 14 51 +50 18 94 30 84 + +19 63 58 72 67 +35 93 29 91 0 +39 26 43 84 21 +70 42 2 53 12 +59 99 8 1 86 + +23 86 34 22 65 +71 10 16 50 91 +66 89 49 81 43 +40 7 26 75 61 +62 59 2 46 95 + +24 21 0 49 25 +92 42 48 12 7 +81 93 59 68 3 +14 23 63 39 29 +35 43 6 44 89 + +67 74 95 34 10 +39 90 59 44 51 +17 16 97 24 62 +20 54 76 63 88 +87 66 14 78 82 + +96 86 67 59 79 +66 3 30 77 71 + 2 91 99 82 31 +48 65 75 98 53 +63 54 64 76 1 + +85 96 40 98 24 +16 20 10 23 17 +79 59 53 42 65 +67 2 5 80 75 +62 38 19 74 73 + +43 10 79 92 8 +52 36 4 5 67 +56 29 33 24 97 +85 17 53 75 65 +62 64 1 21 83 + +93 92 79 17 12 +40 88 6 82 34 +90 96 53 25 43 +14 62 54 10 39 +49 68 41 16 44 + +67 99 24 58 76 +43 53 59 54 51 +47 6 61 8 2 +80 68 90 14 4 +29 46 94 89 50 + +14 45 19 33 43 + 6 55 4 31 80 +51 2 69 68 61 +71 70 79 91 93 +66 18 54 13 87 + + 8 45 61 54 30 +85 16 19 82 37 +56 39 11 47 4 +74 70 10 60 91 +21 63 95 53 72 + +71 21 63 86 27 +53 52 40 23 81 + 2 47 92 68 15 +46 45 31 8 1 +34 80 37 11 69 + +96 0 15 90 66 +65 43 92 83 18 + 3 47 19 8 32 +71 26 42 34 28 +62 99 55 5 12 + +37 99 30 21 3 +63 18 68 47 27 +57 0 65 85 20 + 7 58 40 92 43 +15 19 5 4 53 + +46 16 45 95 68 + 6 44 31 47 73 +84 82 71 75 94 +26 25 17 32 49 +18 96 13 58 9 + +71 36 13 68 10 +84 7 60 79 41 + 1 83 43 81 97 +90 53 80 19 38 +48 25 32 42 29 + +37 68 86 44 78 +87 67 77 70 60 +45 34 27 15 47 +12 21 13 55 26 +81 41 63 40 74 + +24 50 93 94 57 +99 4 56 5 28 +42 31 22 6 76 +90 89 16 49 59 + 9 7 43 71 54 + +69 75 94 38 46 +52 64 50 72 42 +76 63 13 60 10 +99 80 43 33 17 +25 31 4 89 22 + +88 57 22 66 34 +85 16 87 95 59 +73 2 46 5 29 +25 69 53 6 14 +96 77 19 91 43 + +46 99 52 47 76 +89 53 24 13 59 +45 5 1 30 19 +68 25 22 10 73 +42 27 31 0 94 + +42 44 98 89 87 +65 10 80 56 41 + 3 35 95 48 43 +85 97 83 12 94 +50 38 93 47 17 + +16 73 18 81 89 + 6 48 54 93 19 +35 52 88 49 31 +43 79 83 14 28 +50 62 98 26 22 + +38 47 7 20 35 +45 76 63 96 24 +98 53 2 87 80 +83 86 92 48 1 +73 60 26 94 6 + +80 50 29 53 92 +66 90 79 98 46 +40 21 58 38 60 +35 13 72 28 6 +48 76 51 96 12 + +79 80 24 37 51 +86 70 1 22 71 +52 69 10 83 13 +12 40 3 0 30 +46 50 48 76 5 diff --git a/2021/day-04/solution.js b/2021/day-04/solution.js new file mode 100644 index 0000000..375f0ad --- /dev/null +++ b/2021/day-04/solution.js @@ -0,0 +1,74 @@ +const fs = require('fs') +const path = require('path') +const filePath = path.join(__dirname, 'input.txt') +const { linesToArray, parseData } = require('../../2018/inputParser') +const { markBoard, checkWinner, scoreBoard } = require('./bingo') + +fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => { + if (err) throw err + + initData = linesToArray(initData.trim()) + + const resetInput = () => { + // Deep copy to ensure we aren't mutating the original data + const data = JSON.parse(JSON.stringify(initData)) + + // split up data + const draws = parseData(data.shift()) + const boards = [] + for (let x = 0; x < data.length; x = x + 5) { + boards.push( + data.slice(x, x + 5).map(parseData) + ) + } + + return { + boards, + draws + } + } + + const part1 = () => { + const data = resetInput() + + let winner = false + let draw = -1 + while (winner === false && draw < data.draws.length) { + // next draw + draw++ + + // Mark each board that has the number + console.debug(`Checking draw ${data.draws[draw]}`) + data.boards = data.boards.map((board) => { + return markBoard(board, data.draws[draw]) + }) + + // Check for a winner + data.boards.forEach((board, idx) => { + if (checkWinner(board) === 'winner') { + console.debug(`Board ${idx} is the winner`) + console.debug(data.boards[idx]) + winner = idx + } + }) + } + + // winner found + console.debug(`Score is ${scoreBoard(data.boards[winner])}`) + return scoreBoard(data.boards[winner]) * data.draws[draw] + } + + const part2 = () => { + // const data = resetInput() + // console.debug(data) + return 'No answer yet' + } + const answers = [] + answers.push(part1()) + answers.push(part2()) + + answers.forEach((ans, idx) => { + console.info(`-- Part ${idx + 1} --`) + console.info(`Answer: ${ans}`) + }) +}) diff --git a/index.js b/index.js index b752549..95257dc 100644 --- a/index.js +++ b/index.js @@ -1 +1 @@ -require('./2021/day-03/solution') +require('./2021/day-04/solution')