Advent of Code 2021 - Day 4

Maybe it wants to play bingo?

Day 4a

Some new advent library functions:

const is = (x: any) => x !== '' && x !== null && x !== undefined
const parse10 = (s: string) => parseInt(s, 10)
const clean = (s: string) => s.trim().replace(/^[^\S\n]+/gm, '')
const paragraphs = (s: string) => clean(s).split(/\n{2,}/)
const ints = (s: string) => clean(s).split(/\D+/).map(parse10)
const max = (ns: number[]) => Math.max.apply(null, ns)
const min = (ns: number[]) => Math.min.apply(null, ns)
const sum = (ns: number[]) => reduce(ns, (sum, n) => sum + n, 0)

const ranks = <T>(xs: T[], n: number): T[][] =>
  R.times(Math.ceil(xs.length / n), i => xs.slice(n * i, n * (i + 1)))

const files = <T>(xs: T[], n: number): T[][] =>
  R.times(Math.ceil(xs.length / n), i =>
    R.times(n, j => xs[i + j * n]).filter(is),
  )

And my solution:

const d4 = ({
  input = inputs.d4,
  dbg,
  pick,
}: DayProps & {pick: typeof R.last}) => {
  const {draws, boards} = R.pipe(paragraphs(input), ([ds, ...bs]) => ({
    draws: ints(ds),
    boards: bs.map(ints),
  }))
  const edge = Math.sqrt(boards[0].length)

  // Reverse lookup from a drawn number to its position in line.
  const order = reduce.indexed(
    R.uniq(draws),
    (order, draw, i) => ((order[draw] = i), order),
    [] as number[],
  )

  // Boards sorted in the order they win.
  const bingos = R.pipe(
    boards,
    R.map.indexed((board, num) => {
      const rows = [...ranks(board, edge), ...files(board, edge)]
      const orders = rows.map(r => r.map(draw => order[draw]))
      const turn = min(orders.map(max))
      return {board, num, rows, orders, turn}
    }),
    R.sort((a, b) => a.turn - b.turn),
  )

  // Pick the winning (or losing) board.
  const winning = pick(bingos)

  // Unmarked numbers are those that haven't been called by the winning.turn
  const unmarked = sum(winning.board.filter(x => order[x] > winning.turn))

  // Which number was called on the winning turn?
  const winner = draws[winning.turn]

  dbg({...R.pick(winning, ['num', 'turn']), unmarked, winner})
  return `Final score: ${unmarked * winner}`
}

export const d4a = (props: DayProps) => d4({...props, pick: R.first})

Sample output:

My output:

Day 4b

Got lucky here with my implementation for the first part:

export const d4b = (props: DayProps) => d4({...props, pick: R.last})

Sample output:

My output: