day04.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import * as O from "fp-ts/Option";
  2. import * as N from "fp-ts/number";
  3. import { chunksOf, concat, every, exists, filter, findFirst, isOutOfBound, map, mapWithIndex, reduce, replicate, tail } from "fp-ts/lib/Array";
  4. import { pipe } from "fp-ts/lib/function";
  5. import { concatAll } from "fp-ts/lib/Monoid";
  6. import { isEmpty } from "fp-ts/lib/string";
  7. import { draws as testDraws, RawTable, tables as testTables } from "./input.test";
  8. import { draws as inputDraws, tables as inputTables } from "./input";
  9. type Value = {
  10. value: number,
  11. marked: boolean
  12. };
  13. type Table = {
  14. id: number;
  15. lastMarked?: number,
  16. won?: number;
  17. table: Value[][]
  18. }
  19. const parseTables = (tables: string) => pipe(
  20. tables.split("\n"),
  21. filter(s => !isEmpty(s)),
  22. chunksOf(5),
  23. map(table => pipe(table, map(row => row.split(" ").filter(n => !isEmpty(n)).map(s => parseInt(s)))))
  24. );
  25. const tables: Table[] = pipe(
  26. // testTables,
  27. parseTables(inputTables),
  28. mapWithIndex<RawTable, Table>((i, t) => ({
  29. id: i,
  30. table: pipe(t, map(r => pipe(r, map<number, Value>(v => ({ value: v, marked: false })))))
  31. }))
  32. );
  33. const draws: number[] =
  34. // testDraws;
  35. inputDraws;
  36. const product = (a: number) => (b: number) =>
  37. N.MonoidProduct.concat(a, b);
  38. const result = (table: Table): number => pipe(
  39. [...table.table],
  40. reduce([] as Value[], (all, curr) => pipe(all, concat(curr))),
  41. filter(v => !v.marked),
  42. map(v => v.value),
  43. concatAll(N.MonoidSum),
  44. product(table.lastMarked ?? 1)
  45. );
  46. const evalRows = (table: Table): O.Option<number> => pipe(
  47. [...table.table],
  48. map(every(v => v.marked)),
  49. reduce(false, (acc, r) => acc || r),
  50. O.guard,
  51. O.map(() => result(table))
  52. );
  53. const evalColumns = (table: Table): O.Option<number> => pipe(
  54. [...table.table],
  55. reduce(
  56. replicate(table.table[0].length, true),
  57. (marks, row) => pipe(row, map(v => v.marked), mapWithIndex((i, m) => marks[i] && m))
  58. ),
  59. exists(m => m),
  60. O.guard,
  61. O.map(() => result(table))
  62. );
  63. const evalTable = (table: Table): Table => pipe(
  64. table,
  65. evalRows,
  66. O.alt(() => evalColumns(table)),
  67. O.fold(
  68. () => ({...table}),
  69. n => ({...table, won: n})
  70. )
  71. );
  72. const findValue = (draw: number) => (table: Table): O.Option<Value> => pipe(
  73. [...table.table],
  74. map(findFirst((v: Value) => v.value === draw)),
  75. reduce(O.none as O.Option<Value>, (v, o) => pipe(v, O.alt(() => o)))
  76. );
  77. const calc = (tables: Table[]) => (draw: number): Table[] => pipe(
  78. tables,
  79. filter(t => t.won === undefined),
  80. map(t => pipe(
  81. t,
  82. findValue(draw),
  83. O.fold<Value, Table>(
  84. () => ({
  85. ...t,
  86. lastMarked: undefined
  87. }),
  88. v => {
  89. v.marked = true;
  90. return {
  91. ...t,
  92. lastMarked: draw
  93. };
  94. }
  95. ),
  96. evalTable
  97. ))
  98. );
  99. const play = (ts: Table[], ds: number[]): Table[] => {
  100. if(isOutOfBound(0, ds)) {
  101. return ts;
  102. }
  103. const newTables = calc(ts)(ds[0]);
  104. console.log("new winners:", pipe(newTables, filter(t => t.won !== undefined)));
  105. return play(newTables, pipe(ds, tail, O.getOrElse(() => [] as number[])));
  106. }
  107. play(tables, draws);