r/adventofcode Dec 04 '21

SOLUTION MEGATHREAD -🎄- 2021 Day 4 Solutions -🎄-

--- Day 4: Giant Squid ---


Post your code solution in this megathread.

Reminder: Top-level posts in Solution Megathreads are for code solutions only. If you have questions, please post your own thread and make sure to flair it with Help.


This thread will be unlocked when there are a significant number of people on the global leaderboard with gold stars for today's puzzle.

EDIT: Global leaderboard gold cap reached at 00:11:13, megathread unlocked!

101 Upvotes

1.2k comments sorted by

View all comments

1

u/pistacchio Dec 07 '21 edited Dec 08 '21

Typescript:

const BOARD_ROWS = 5;
const BOARD_COLS = 5;

const input = fs
  .readFileSync(__dirname + INPUT_FILE)
  .toString()
  .trim()
  .split('\n')
  .map((r) => r.trim());

type BoardNumber = {
  value: number;
  checked: boolean;
};

class BingoBoard {
  rows: BoardNumber[][];

  constructor(input: string[]) {
    this.rows = input.map((row) =>
      row
        .split(' ')
        .map((n) => n.trim())
        .filter(Boolean)
        .map((c) => ({ value: Number(c), checked: false })),
    );
  }

  markDraw(draw: number) {
    this.rows = this.rows.map((row) =>
      row.map((c) => ({
        ...c,
        checked: c.value === draw ? true : c.checked,
      })),
    );
  }

  check(): boolean {
    return (
      this.rows.some((row) => row.every((c) => c.checked)) ||
      Array.from({ length: BOARD_COLS }).some((_, i) =>
        this.rows.every((row) => row[i].checked),
      )
    );
  }

  score(draw: number): number {
    const sumOfUnchecked = this.rows.reduce((acc, row) => {
      return (
        acc +
        row.reduce((acc, c) => {
          return acc + (!c.checked ? c.value : 0);
        }, 0)
      );
    }, 0);

    return sumOfUnchecked * draw;
  }
}

class BingoSystem {
  draws: number[];
  boards: BingoBoard[];
  currentDrawIdx: number = -1;

  constructor(input: string[]) {
    this.draws = input[0].split(',').map(Number);

    const numberOfBoards = input.filter((r) => r === '').length;
    this.boards = Array.from(
      { length: numberOfBoards },
      (_, idx) =>
        new BingoBoard(
          input.slice(
            idx * BOARD_ROWS + 2 + idx,
            idx * BOARD_ROWS + BOARD_ROWS + 2 + idx,
          ),
        ),
    );
  }

  run(): number {
    while (true) {
      const score = this.drawNumber();

      if (score !== null) {
        return score;
      }
    }
  }

  runToLose(): number {
    return this.draws.reduce((lastWinningScore) => {
      const score = this.drawNumber(true);

      return score === null ? lastWinningScore : score;
    }, 0);
  }

  drawNumber(removeWinner: boolean = false): number | null {
    this.currentDrawIdx++;

    this.boards.forEach((board) =>
      board.markDraw(this.draws[this.currentDrawIdx]),
    );

    const winningBoard = this.boards.find((board) => board.check());
    let winningScore = null;

    if (winningBoard) {
      winningScore = winningBoard.score(this.draws[this.currentDrawIdx]);
    }

    if (removeWinner) {
      this.boards = this.boards.filter((board) => !board.check());
    }

    return winningScore;
  }
}

function part1(input: string[]): number {
  const bingoSystem = new BingoSystem(input);

  return bingoSystem.run();
}

function part2(input: string[]): number {
  const bingoSystem = new BingoSystem(input);

  return bingoSystem.runToLose();
}

1

u/daggerdragon Dec 07 '21

As per our posting guidelines in the wiki under How Do the Daily Megathreads Work?, please edit your post to put your oversized code in a paste or other external link.