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
6572func (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
69107type 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
158198type cab struct {
159- pos position
160199 curDir direction
200+ n navigator
161201}
162202
163203func 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+
172216func (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+
207300func 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+ }
0 commit comments