Skip to content

Commit c0a35c5

Browse files
committed
feat(2016/day01): Implement part2
1 parent 3ff5e7c commit c0a35c5

File tree

3 files changed

+208
-25
lines changed

3 files changed

+208
-25
lines changed

internal/puzzles/solutions/2016/day01/solution.go

Lines changed: 184 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"regexp"
99
"strconv"
1010
"strings"
11+
"sync"
1112

1213
"github.com/obalunenko/advent-of-code/internal/puzzles"
1314
)
@@ -45,6 +46,10 @@ func (s solution) Part1(input io.Reader) (string, error) {
4546

4647
c := newCab()
4748

49+
go func() {
50+
c.n.start()
51+
}()
52+
4853
cmds := strings.Split(buf.String(), ", ")
4954
for _, cmd := range cmds {
5055
t, s, err := splitCommand(cmd)
@@ -57,13 +62,46 @@ func (s solution) Part1(input io.Reader) (string, error) {
5762
}
5863
}
5964

60-
l := c.Pos().manhattan()
65+
c.n.stop()
66+
67+
l := c.n.Pos().manhattan()
6168

6269
return strconv.Itoa(l), nil
6370
}
6471

6572
func (s solution) Part2(input io.Reader) (string, error) {
66-
return "", puzzles.ErrNotImplemented
73+
buf := new(bytes.Buffer)
74+
if _, err := buf.ReadFrom(input); err != nil {
75+
return "", fmt.Errorf("failed to read input: %w", err)
76+
}
77+
78+
c := newCab()
79+
80+
go c.n.start()
81+
82+
cmds := strings.Split(buf.String(), ", ")
83+
for _, cmd := range cmds {
84+
t, s, err := splitCommand(cmd)
85+
if err != nil {
86+
return "", fmt.Errorf("split command: %w", err)
87+
}
88+
89+
if err = c.Move(t, s); err != nil {
90+
return "", fmt.Errorf("move: %w", err)
91+
}
92+
}
93+
94+
c.n.stop()
95+
96+
rl := c.n.revisitedList()
97+
if len(rl) == 0 {
98+
return "", errors.New("no revisited points")
99+
}
100+
101+
// get first
102+
l := rl[0].manhattan()
103+
104+
return strconv.Itoa(l), nil
67105
}
68106

69107
type turn string
@@ -73,14 +111,16 @@ const (
73111
rightTurn = "R"
74112
)
75113

76-
func tunrFromstring(s string) (turn, error) {
114+
var errInvalidTurn = errors.New("invalid turn value")
115+
116+
func turnFromstring(s string) (turn, error) {
77117
switch s {
78118
case leftTurn:
79119
return leftTurn, nil
80120
case rightTurn:
81121
return rightTurn, nil
82122
default:
83-
return "", errors.New("invalid turn value")
123+
return "", errInvalidTurn
84124
}
85125
}
86126

@@ -156,41 +196,92 @@ func (d direction) strikeTo(t turn) direction {
156196
}
157197

158198
type cab struct {
159-
pos position
160199
curDir direction
200+
n navigator
161201
}
162202

163203
func newCab() cab {
164204
return cab{
165-
pos: position{
166-
x: 0,
167-
y: 0,
168-
},
169205
curDir: northDirection,
206+
n: newNavigator(),
170207
}
171208
}
209+
210+
var errInvalidDirect = errors.New("invalid direction")
211+
212+
const (
213+
step = 1
214+
)
215+
172216
func (c *cab) Move(t turn, steps int) error {
173217
c.curDir = c.curDir.strikeTo(t)
174218
if !c.curDir.isValid() {
175-
return errors.New("invalid direction")
219+
return errInvalidDirect
176220
}
177221

178222
switch c.curDir {
179223
case northDirection:
180-
c.pos.addY(steps)
224+
c.n.moveNorth(steps)
181225
case eastDirection:
182-
c.pos.addX(steps)
226+
c.n.moveEast(steps)
183227
case southDirection:
184-
c.pos.subY(steps)
228+
c.n.moveSouth(steps)
185229
case westDirection:
186-
c.pos.subX(steps)
230+
c.n.moveWest(steps)
187231
}
188232

189233
return nil
190234
}
191235

192-
func (c cab) Pos() position {
193-
return c.pos
236+
func (n *navigator) moveNorth(steps int) {
237+
for i := 0; i < steps; i++ {
238+
n.mu.Lock()
239+
n.pos.addY(step)
240+
n.mu.Unlock()
241+
242+
n.record <- n.Pos()
243+
}
244+
}
245+
246+
func (n *navigator) moveEast(steps int) {
247+
for i := 0; i < steps; i++ {
248+
n.mu.Lock()
249+
n.pos.addX(step)
250+
n.mu.Unlock()
251+
252+
n.record <- n.Pos()
253+
}
254+
}
255+
256+
func (n *navigator) moveSouth(steps int) {
257+
for i := 0; i < steps; i++ {
258+
n.mu.Lock()
259+
n.pos.subY(step)
260+
n.mu.Unlock()
261+
262+
n.record <- n.Pos()
263+
}
264+
}
265+
266+
func (n *navigator) moveWest(steps int) {
267+
for i := 0; i < steps; i++ {
268+
n.mu.Lock()
269+
n.pos.subX(step)
270+
n.mu.Unlock()
271+
272+
n.record <- n.Pos()
273+
}
274+
}
275+
276+
func (c *cab) Track() track {
277+
return c.n.track
278+
}
279+
280+
func (n navigator) Pos() position {
281+
n.mu.Lock()
282+
defer n.mu.Unlock()
283+
284+
return n.pos
194285
}
195286

196287
// Example: L4, R5
@@ -204,20 +295,93 @@ const (
204295
totalMatchesNum = 3
205296
)
206297

298+
var errInvalidCMD = errors.New("invalid command")
299+
207300
func splitCommand(cmd string) (turn, int, error) {
208301
parts := re.FindStringSubmatch(cmd)
209302
if len(parts) != totalMatchesNum {
210-
return "", 0, errors.New("invalid command")
303+
return "", 0, errInvalidCMD
211304
}
212305

213-
t, err := tunrFromstring(parts[turnPos])
306+
t, err := turnFromstring(parts[turnPos])
214307
if err != nil {
215-
return "", 0, errors.New("invalid turn")
308+
return "", 0, fmt.Errorf("turnFromstring: %w", err)
216309
}
217310
s, err := strconv.Atoi(parts[stepsPos])
218311
if err != nil {
219-
return "", 0, errors.New("invalid steps num")
312+
return "", 0, fmt.Errorf("invalid steps num: %w", err)
220313
}
221314

222315
return t, s, nil
223316
}
317+
318+
type navigator struct {
319+
record chan position
320+
pos position
321+
track track
322+
mu *sync.Mutex
323+
wg *sync.WaitGroup
324+
revisited []position
325+
}
326+
327+
func (n *navigator) recordTrack(p position) {
328+
n.mu.Lock()
329+
330+
defer func() {
331+
n.mu.Unlock()
332+
}()
333+
334+
if n.track.isVisited(p) {
335+
n.revisited = append(n.revisited, p)
336+
}
337+
338+
n.track.record(p)
339+
}
340+
341+
func (n *navigator) start() {
342+
n.wg.Add(1)
343+
344+
for p := range n.record {
345+
n.recordTrack(p)
346+
}
347+
348+
n.wg.Done()
349+
}
350+
351+
func (n *navigator) stop() {
352+
close(n.record)
353+
354+
n.wg.Wait()
355+
}
356+
357+
func (n navigator) revisitedList() []position {
358+
return n.revisited
359+
}
360+
361+
func newNavigator() navigator {
362+
return navigator{
363+
record: make(chan position),
364+
pos: position{
365+
x: 0,
366+
y: 0,
367+
},
368+
track: newTrack(),
369+
mu: &sync.Mutex{},
370+
wg: &sync.WaitGroup{},
371+
revisited: []position{},
372+
}
373+
}
374+
375+
type track map[position]bool
376+
377+
func newTrack() track {
378+
return make(track)
379+
}
380+
381+
func (t track) record(p position) {
382+
t[p] = true
383+
}
384+
385+
func (t track) isVisited(p position) bool {
386+
return t[p]
387+
}

internal/puzzles/solutions/2016/day01/solution_test.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ func Test_solution_Part1(t *testing.T) {
8282
}
8383

8484
func Test_solution_Part2(t *testing.T) {
85-
t.Skip()
86-
8785
type fields struct {
8886
name string
8987
}
@@ -99,7 +97,17 @@ func Test_solution_Part2(t *testing.T) {
9997
want string
10098
wantErr bool
10199
}{
102-
{},
100+
{
101+
name: "",
102+
fields: fields{
103+
name: "",
104+
},
105+
args: args{
106+
input: strings.NewReader("R8, R4, R4, R8"),
107+
},
108+
want: "4",
109+
wantErr: false,
110+
},
103111
}
104112

105113
for _, tt := range tests {

internal/puzzles/solutions/2016/day01/spec.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# --- Day 1: No Time for a Taxicab ---
22

3+
## Part One
4+
35
Santa's sleigh uses a very high-precision clock to guide its movements, and the clock's oscillator
46
is regulated by stars. Unfortunately, the stars have been stolen... by the Easter Bunny.
57
To save Christmas, Santa needs you to retrieve all fifty stars by December 25th.
@@ -13,7 +15,7 @@ You're airdropped near Easter Bunny Headquarters in a city somewhere.
1315
Document the Elves intercepted start here, and nobody had time to work them out further.
1416

1517
The Document indicates that you should start at the given coordinates (where you just landed) and face North.
16-
Then, follow the provided sequence: either turn left (L) or right (R) 90 degrees,
18+
Then, follow the provided sequence: either turn left `(L)` or right `(R)` 90 degrees,
1719
then walk forward the given number of blocks, ending at a new intersection.
1820

1921
There's no time to follow such ridiculous instructions on foot, though,
@@ -25,4 +27,13 @@ For example:
2527
- Following `R2, L3` leaves you `2` blocks East and `3` blocks North, or `5` blocks away.
2628
- `R2, R2, R2` leaves you `2` blocks due South of your starting position, which is `2` blocks away.
2729
- `R5, L5, R5, R3` leaves you `12` blocks away.
28-
How many blocks away is Easter Bunny HQ?
30+
How many blocks away is Easter Bunny HQ?
31+
32+
## Part Two
33+
34+
Then, you notice the instructions continue on the back of the Recruiting Document. Easter Bunny HQ is actually at the first
35+
location you visit twice.
36+
37+
For example, if your instructions are `R8, R4, R4, R8`, the first location you visit twice is `4` blocks away, due `East`.
38+
39+
How many blocks away is the first location you visit twice?

0 commit comments

Comments
 (0)