-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday15_1.nim
134 lines (112 loc) · 4.46 KB
/
day15_1.nim
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
import os
if paramCount() != 1:
echo "Usage: ./dayXX <input file>"
quit(1)
let filename = paramStr(1)
if not fileExists(filename):
echo "File not found: ", filename
quit(1)
# RUN: FULL
## RUN: TEST
import re
import sequtils
import strutils
type
Point = tuple[x, y: int]
func newPoint(x, y: int): Point {.inline.} = (x: x, y: y)
var data: seq[tuple[sensor: Point, beacon: Point]]
func `+`(a, b: Point): Point {.inline.} = (a.x + b.x, a.y + b.y)
func `+=`(a: var Point, b: Point) {.inline.} = a = a + b
func `-`(a, b: Point): Point {.inline.} = (a.x - b.x, a.y - b.y)
func `-=`(a: var Point, b: Point) {.inline.} = a = a - b
func abs(a: Point): int {.inline.} = abs(a.x) + abs(a.y)
for line in lines(filename):
var matches: array[4, string]
# Sensor at x=2, y=18: closest beacon is at x=-2, y=15
if not match(line, re"Sensor at x=([+-]?\d+), y=([+-]?\d+): closest beacon is at x=([+-]?\d+), y=([+-]?\d+)", matches):
echo "Invalid line: ", line
quit(1)
let parsed_matches = matches.toSeq.map(parseInt)
data.add((
sensor: (x: parsed_matches[0], y: parsed_matches[1]),
beacon: (x: parsed_matches[2], y: parsed_matches[3])
))
# echo "Sensor at ", sensor, " is closest to ", beacon
var
enclosing_points: seq[Point]
sensors: seq[Point]
beacons: seq[Point]
distances: seq[int]
for sensor, beacon in data.items:
enclosing_points.add(sensor)
enclosing_points.add(beacon)
let distance = abs(sensor - beacon)
enclosing_points.add(sensor + newPoint(-distance, 0))
enclosing_points.add(sensor + newPoint(distance, 0))
enclosing_points.add(sensor + newPoint(0, -distance))
enclosing_points.add(sensor + newPoint(0, distance))
distances.add(distance)
sensors.add(sensor)
beacons.add(beacon)
##==================================
##
## #### ##### ### ## ##
## ## ## ## ## ## ### ##
## ### ##### ## ## #### ##
## ## ## ####### ## ####
## #### ## ## ## ## ###
##
##==================================
const INT_MAX = int.high
const INT_MIN = int.low
type Span = tuple[x1, x2, y1, y2: int]
const MIN_SPAN: Span = (INT_MAX, INT_MIN, INT_MAX, INT_MIN)
func span(p: Point): Span {.inline.} = (p.x, p.x, p.y, p.y)
func `+`(a, b: Span): Span {.inline.} = (min(a.x1, b.x1), max(a.x2, b.x2), min(a.y1, b.y1), max(a.y2, b.y2))
func `+=`(a: var Span, b: Span) {.inline.} = a = a + b
func `-`(a: Span, b: Point): Span {.inline.} = (a.x1 - b.x, a.x2 - b.x, a.y1 - b.y, a.y2 - b.y)
func `-=`(a: var Span, b: Point) {.inline.} = a = a - b
func width(s: Span): int {.inline.} = s.x2 - s.x1 + 1
func height(s: Span): int {.inline.} = s.y2 - s.y1 + 1
func `[]`(s: Span, x, y: int): Point {.inline.} = (s.x1 + x, s.y1 + y)
proc span(sp: seq[Point]): Span {.inline.} =
result = MIN_SPAN
for p in sp:
result += p.span
let all_span = enclosing_points.span
# var repr_string: string = ""
# for j in 0..<all_span.height:
# for i in 0..<all_span.width:
# let coords: Point = all_span[i, j]
# if coords in sensors:
# repr_string.add("S")
# elif coords in beacons:
# repr_string.add("B")
# else:
# let coord_distances = sensors.mapIt(abs(it - coords))
# let mask = zip(coord_distances, distances).mapIt(it[0] <= it[1])
# if mask.anyIt(it):
# let sensors_with_index = zip(sensors, (0..<sensors.len).toSeq)
# let closest_index = zip(sensors_with_index, mask).filterIt(it[1]).mapIt(it[0][1])
# assert closest_index.len >= 1
# let closest_index_letter = (closest_index[0] + ord('a')).chr
# repr_string.add(closest_index_letter)
# else:
# repr_string.add(".")
# repr_string.add("\n")
# echo repr_string
# const PROBE_ROW = 10
const PROBE_ROW = 2000000
var not_allowed: int = 0
for i in 0..all_span.width:
let coords: Point = (all_span[i, 0].x, PROBE_ROW)
for (sensor, closest) in zip(sensors, distances):
if abs(sensor - coords) <= closest:
let is_beacon = beacons.filterIt(it == coords).len > 0
if not is_beacon:
# echo sensor, " is closest to ", coords, " (", abs(sensor - coords), " <= ", closest, ")"
not_allowed.inc
break
if i mod 100000 == 0:
echo i, " / ", all_span.width, " not allowed: ", not_allowed
echo not_allowed