I should perhaps clarify -- since there's no actual physical
coordinates involved here. I am using "left handed" and "right handed"
to refer to what should maybe be labeled as the parity of the axes of
the coordinate system.

I really need better vocabulary for talking about these distinctions.

Thanks,

-- 
Raul

On Sun, Jan 9, 2022 at 11:52 PM Raul Miller <rauldmil...@gmail.com> wrote:
>
> Also, ... I believe that that cross product is a right handed cross
> product. It would be interesting to think about what would need to
> change to make a left handed cross product work (for example, the
> cross product implementation in the complete tensor essay in the
> wiki).
>
> (I had thought that I was agnostic to the handedness of the coordinate
> system, but it looks like that was not the case.)
>
> (A trivial fix would be to negate all beacon coordinates along one
> axis. But I am wondering if there is a simple algorithmic fix which
> would retain the original coordinate values...)
>
> Thanks again,
>
> --
> Raul
>
> On Sun, Jan 9, 2022 at 8:36 PM Raul Miller <rauldmil...@gmail.com> wrote:
> >
> > Oops, I thought I had included that in my message. My apologies:
> >
> > NB. from https://code.jsoftware.com/wiki/Phrases/Matrices
> > cross=:(1 _1 1 * 1 (-/ . *)\. ])@,.
> >
> > Good question, thanks.
> >
> > --
> > Raul
> >
> > On Sun, Jan 9, 2022 at 9:49 AM 'Michael Day' via Programming
> > <programm...@jsoftware.com> wrote:
> > >
> > > Raul,  would you mind defining cross?  I've tried */ and (+/  .  *) but
> > > neither seem to
> > > work for me,  and I'm not sure what you mean.
> > >
> > > As I said a few days ago,  I gave up wondering why I'd found 48
> > > rotations when they
> > > asked for 24,  couldn't grasp what the meant by facing" and resorted to
> > > matrix divide
> > > with rounding to yield the orientation matrix in each case.
> > >
> > > Still stuck on day23 part 1!!!
> > >
> > > Cheers,
> > >
> > > Mike
> > >
> > > On 09/01/2022 02:14, Raul Miller wrote:
> > > > https://adventofcode.com/2021/day/19
> > > >
> > > > Like a variety of my work on previous days, my code here is rather
> > > > bulky. Did I mention that Advent of Code was something of a bad habit
> > > > for me?
> > > >
> > > > Anyways, here, not only is the code bulky, even the sample data is 
> > > > bulky.
> > > >
> > > > Basically, for this puzzle, we were given a bunch of coordinates
> > > > beacons reported by a small set of scanners. These coordinates
> > > > contained three "flaws":
> > > >
> > > > (1) They used a coordinate system which was centered on the scanner --
> > > > not a universal coordinate system.
> > > >
> > > > (2) Each scanner had limited range, so no single scan had locations
> > > > for all beacons.
> > > >
> > > > (3) Each scanner had an undetermined orientation.
> > > >
> > > > Luckily for us, each probe "originally" had the same coordinate
> > > > system, and each probe's rotations were multiples of 90 degrees along
> > > > each axis. So there were only 24 possible orientations for each
> > > > scanner.
> > > >
> > > > And, sample data looked like this:
> > > >
> > > > sample=:{{)n
> > > > --- scanner 0 ---
> > > > 404,-588,-901
> > > > 528,-643,409
> > > > -838,591,734
> > > > 390,-675,-793
> > > > -537,-823,-458
> > > > -485,-357,347
> > > > -345,-311,381
> > > > -661,-816,-575
> > > > -876,649,763
> > > > -618,-824,-621
> > > > 553,345,-567
> > > > 474,580,667
> > > > -447,-329,318
> > > > -584,868,-557
> > > > 544,-627,-890
> > > > 564,392,-477
> > > > 455,729,728
> > > > -892,524,684
> > > > -689,845,-530
> > > > 423,-701,434
> > > > 7,-33,-71
> > > > 630,319,-379
> > > > 443,580,662
> > > > -789,900,-551
> > > > 459,-707,401
> > > >
> > > > --- scanner 1 ---
> > > > 686,422,578
> > > > 605,423,415
> > > > 515,917,-361
> > > > -336,658,858
> > > > 95,138,22
> > > > -476,619,847
> > > > -340,-569,-846
> > > > 567,-361,727
> > > > -460,603,-452
> > > > 669,-402,600
> > > > 729,430,532
> > > > -500,-761,534
> > > > -322,571,750
> > > > -466,-666,-811
> > > > -429,-592,574
> > > > -355,545,-477
> > > > 703,-491,-529
> > > > -328,-685,520
> > > > 413,935,-424
> > > > -391,539,-444
> > > > 586,-435,557
> > > > -364,-763,-893
> > > > 807,-499,-711
> > > > 755,-354,-619
> > > > 553,889,-390
> > > >
> > > > --- scanner 2 ---
> > > > 649,640,665
> > > > 682,-795,504
> > > > -784,533,-524
> > > > -644,584,-595
> > > > -588,-843,648
> > > > -30,6,44
> > > > -674,560,763
> > > > 500,723,-460
> > > > 609,671,-379
> > > > -555,-800,653
> > > > -675,-892,-343
> > > > 697,-426,-610
> > > > 578,704,681
> > > > 493,664,-388
> > > > -671,-858,530
> > > > -667,343,800
> > > > 571,-461,-707
> > > > -138,-166,112
> > > > -889,563,-600
> > > > 646,-828,498
> > > > 640,759,510
> > > > -630,509,768
> > > > -681,-892,-333
> > > > 673,-379,-804
> > > > -742,-814,-386
> > > > 577,-820,562
> > > >
> > > > --- scanner 3 ---
> > > > -589,542,597
> > > > 605,-692,669
> > > > -500,565,-823
> > > > -660,373,557
> > > > -458,-679,-417
> > > > -488,449,543
> > > > -626,468,-788
> > > > 338,-750,-386
> > > > 528,-832,-391
> > > > 562,-778,733
> > > > -938,-730,414
> > > > 543,643,-506
> > > > -524,371,-870
> > > > 407,773,750
> > > > -104,29,83
> > > > 378,-903,-323
> > > > -778,-728,485
> > > > 426,699,580
> > > > -438,-605,-362
> > > > -469,-447,-387
> > > > 509,732,623
> > > > 647,635,-688
> > > > -868,-804,481
> > > > 614,-800,639
> > > > 595,780,-596
> > > >
> > > > --- scanner 4 ---
> > > > 727,592,562
> > > > -293,-554,779
> > > > 441,611,-461
> > > > -714,465,-776
> > > > -743,427,-804
> > > > -660,-479,-426
> > > > 832,-632,460
> > > > 927,-485,-438
> > > > 408,393,-506
> > > > 466,436,-512
> > > > 110,16,151
> > > > -258,-428,682
> > > > -393,719,612
> > > > -211,-452,876
> > > > 808,-476,-593
> > > > -575,615,604
> > > > -485,667,467
> > > > -680,325,-822
> > > > -627,-443,-432
> > > > 872,-547,-609
> > > > 833,512,582
> > > > 807,604,487
> > > > 839,-516,451
> > > > 891,-625,532
> > > > -652,-548,-490
> > > > 30,-46,-14
> > > > }}
> > > >
> > > > There will be a quiz later.  (Ok, maybe not...)
> > > >
> > > > So, first thing we need here is a mechanism to parse this into
> > > > something J can handle. And, it's not obvious here, but the number of
> > > > beacons reported by each scanner was not the same. This means we need
> > > > to put the scans in boxes so they are not padded.
> > > >
> > > > I decided I also wanted my parsing routine (which I named 'get') to be
> > > > idempotent. This would let me use parsed or unparsed data on the
> > > > command line -- if it was parsed already, 'get' would leave it alone.
> > > >
> > > > get=: {{
> > > >     if. -.'literal'-:datatype y do. y return. end.
> > > >     s=. getscan;._2 rplc&(',';' ';(LF,'---');LF,'!---') y,'!'
> > > >     {:"1 s   assert. (i.#s) -: ;{."1 s
> > > > }}
> > > >
> > > > digits=: ".@([ -. -.)&(~.":i.10)
> > > >
> > > > getscan=: {{
> > > >    (id=. digits 0{::lines); coords=. __&".@>@}. lines=. (<;._2 y)-.a:
> > > > }}
> > > >
> > > > I could have made get and getscan a little simpler, by ignoring the
> > > > reported scan number. But I was working on this puzzle late at night
> > > > and it was easy for me to make mistakes.
> > > >
> > > > The next problem was to piece scans together to form the complete list
> > > > of beacons (using the coordinate system of the scanner #0).
> > > >
> > > > There's a variety of approaches which could have been used here. Mine 
> > > > was this:
> > > >
> > > > (a) I would measure the distance between each pair of *beacons*
> > > > reported by each scanner. The distance between any beacon pair was
> > > > guaranteed to be fixed for every scanner which included both of those
> > > > beacons.
> > > >
> > > > (b) For each pair of *scanners*, I would find and count the beacon
> > > > distance lists which they had in common. Scanners which were closer
> > > > together should have more beacons in common with each other than
> > > > scanners which were farther apart.
> > > >
> > > > Looking at the data, I saw that most scanners had at least one other
> > > > scanner which reported 67 or 68 of the same beacon distances, the next
> > > > most frequent commonality was 17 or 18 of the same beacon distances.
> > > > So, in code, I only concerned myself with scanners which had at least
> > > > half as many beacons in common as the largest reported commonality.
> > > >
> > > > (c) For these pairs, I would find the orientation of the second based
> > > > on the orientation of the first, using the beacons which they had in
> > > > common (the ones whose distances were the in each of the pair of
> > > > scans).
> > > >
> > > > In particular, I decided that I could ignore two problems:
> > > >
> > > > (*) I was not concerned with "spoofed distances". I had enough
> > > > information that I believed that coincidental distance matches would
> > > > not be a problem for me.
> > > >
> > > > (*) I was not concerned with "spoofed orientations". I had enough
> > > > information that I believed that there would be one and only one
> > > > orientation that best fit the beacons for the scanner pairs I was
> > > > considering.
> > > >
> > > > That left me with the problem of representing intermediate results.
> > > >
> > > > The first step -- getting distance pairs between beacons reported by a
> > > > single scanner -- was straightforward:
> > > >
> > > > signature=:{{+/&.:*: |:-"1/~ y}}
> > > >
> > > >     usigs=: ~.@,@signature each scans
> > > >     rough=:  +/@(e.,)&>/~ usigs
> > > >
> > > > The next step - identifying pairs of scanners which were close to each
> > > > other - had one minor trick: each scanner was very, very close to
> > > > itself. And I needed to ignore that.
> > > >
> > > > In other words, I multiplied by the logical negation of the identity 
> > > > matrix
> > > >
> > > >     rough=:  (* -.@=@i.@#)+/@(e.,)&>/~ usigs
> > > >
> > > > Actually, though, I did not need the counts of close beacons -- I just
> > > > wanted to identify the scanners which had a lot of beacons in common
> > > >
> > > >     rough=: (> -:@(>./)@,) (* -.@=@i.@#)+/@(e.,)&>/~ usigs
> > > >
> > > > Next, I wanted to visit those pairs in sequence, starting from scanner
> > > > zero. There's a variety of ways of doing this, but one way is to use
> > > > rough as a connection matrix. Then (+./ .*.)&rough given a selection
> > > > mask would lead us to the connected scanners. These connections are
> > > > bidirectional, and we want to know how soon we can visit a scanner. We
> > > > can use ^: to iterate some number of times and then </\ to find the
> > > > first 1 in each column. My approach here included a lot of extra blank
> > > > rows, which I suppressed using ~.
> > > >
> > > >     ~.</\(+./ .*)&rough^:(i.#rough) (#rough){.1
> > > >
> > > > Finally, I can't sort on a list of bitmasks, but I can convert them to
> > > > an ordered list of indices that represent the order I want to visit
> > > > the scanners.
> > > >
> > > >     order=: ;<@I. ~.</\(+./ .*)&rough^:(i.#rough) (#rough){.1
> > > >
> > > > I'm not quite ready to compare scanner pairs yet, though -- I need to
> > > > extract the "close scanner pair" list from rough, and sort them:
> > > >
> > > >     close=: (/: order&i.) ($#:I.@,)rough
> > > >
> > > > But that approach actually lists each scanner pair twice (0 7 and 7
> > > > 0), and that's not necessary. So, I could do:
> > > >
> > > >     close=: ~. /:~"1 (/: order&i.) ($#:I.@,)rough
> > > >
> > > > or
> > > >
> > > >     close=: (/: order&i.) ($#:I.@,) (* </~@i.@#)rough
> > > >
> > > > With that setup, I am *almost* ready to walk through my scanner pairs.
> > > > But I still need to represent their orientation and offset.
> > > >
> > > > Each scanner has both an offset from the global origin (scanner 0),
> > > > and an orientation with respect to scanner 0. The offset is easy to
> > > > represent -- that's just an x,y,z value. The orientation, though, ...
> > > > that I represented as a permutation of the axis along with a sign
> > > > (positive or negative) for each axis.
> > > >
> > > > reorient=:{{
> > > >    's a'=.8 6#:{.x
> > > >    off=. }.4{.x
> > > >    sign=. _1^2 2 2#:s
> > > >    off+"1 sign*"1 (a A. i.3) {"1 y
> > > > }}
> > > >
> > > > Here, x is the orientation and offset for the scanner, and y is the
> > > > list of beacon coordinates to be manipulated.
> > > >
> > > > This is not an optimal representation. The problem with this approach
> > > > is that it can represent both right handed and left handed coordinate
> > > > systems, and I only wanted coordinate systems which could result from
> > > > a rotation of the origin coordinate system.
> > > >
> > > > I did not know whether that origin system was right handed or left
> > > > handed (since I didn't have any external reference data), all I cared
> > > > was that my alternate orientations matched. To solve this I did a dry
> > > > run, using an identity matrix in place of beacon coordinates, and 0
> > > > offsets. If a cross product of the first two result coordinates
> > > > matched the third, I had a viable orientation:
> > > >
> > > > plausible=: I.({:-:cross/@{.~&2)@reorient&(=i.3)"1 ,.i.48
> > > >
> > > > Still with me? I got to this point mostly by trial and error, with
> > > > lots of asserts in my code to make me aware of invalid assumptions.  I
> > > > have since deleted most of those mistakes and the assertions which
> > > > made me aware of those problems. And, I've cleaned up some of my code
> > > > a bit from my initial puzzle solution, so that it would be readable.
> > > > But I am not sure how well I am explaining my resultant
> > > > understanding...
> > > >
> > > > The next stage of puzzle solving was, for each pair of scans:
> > > >
> > > > (1) find the beacon coordinate pairs in each which share a distance
> > > > between the scanner pair.
> > > >
> > > > (2) Try each of the plausible orientations (with zero offsets),
> > > > subtracting the selected parts of the scanner set. Since the beacon
> > > > coordinate pairs are ordered by distance between the pair, they are
> > > > ordered the same for each scanner set. And, when I get the orientation
> > > > right, distance between pairs from the two sets will mostly be the
> > > > same distance apart (this will be the distance between the two
> > > > scanners).
> > > >
> > > > My implementation is still a bit sloppy here, but it gets the job done:
> > > >
> > > > whichori=:{{
> > > >    d=. #@~.@:(-&x)@reorient&y"1,.plausible
> > > >    plausible{~d i.<./d
> > > > }}
> > > >
> > > > relori=: {{
> > > >    'c0 c1'=. y
> > > >    sigs=. signature@>y
> > > >    k=. +/4=#/.~,sigs
> > > >    ref=. k{.}. (~. \: #/.~),sigs
> > > >    ids=. (* k&>) ref i. sigs
> > > >    pairs0=. pairs1=. i.0 0
> > > >    for_id. 1+i.k-1 do.
> > > >      j=. (}.$sigs)#:I.,id=ids
> > > >      assert.4=#j
> > > >      pairs0=. pairs0,(2{.j) { c0
> > > >      pairs1=. pairs1,(2}.j) { c1
> > > >    end.
> > > >    o=. pairs0 whichori pairs1
> > > >    candidates=./:~(#/.~,.~.),/pairs0 -"1 o reorient pairs1
> > > >    assert. ~:_2{.{."1 candidates
> > > >    off=.}.{:candidates
> > > >    o,off
> > > > }}
> > > >
> > > > Note that when I wrote this code I was not completely sure that I
> > > > would not have "spoofed distances". So I am not actually completely
> > > > ignoring that problem -- the assert. 4=#j in the middle of relori
> > > > would fail if I somehow had the same distance between more than a pair
> > > > of beacons. (It's 4, rather than 2, because each pairing is
> > > > represented twice -- I found the distance between each pair of beacons
> > > > and beaconA is the same distance from beaconB that beaconB is from
> > > > beaconA.)
> > > >
> > > > It would be fun working out how to express this more concisely, but I
> > > > haven't thought enough about that issue.
> > > >
> > > > And, that's all the pieces I needed for part A, which was to count the
> > > > total number of scanned beacons:
> > > >
> > > > a19=:{{
> > > >     scans=: get y
> > > >     heritage=:(,;0);(<:#scans)#<i.0 0
> > > >     usigs=: ~.@,@signature each scans
> > > >     rough=: (> -:@(>./)@,) (* -.@=@i.@#)+/@(e.,)&>/~ usigs
> > > >     order=: ;<@I. ~.</\(+./ .*)&rough^:(i.#rough) (#rough){.1
> > > >     close=: ~. /:~"1 (/: order&i.) ($#:I.@,)rough
> > > >     known=:,:0 0 0 0 0
> > > >     for_scanpair.close do.
> > > >         'kid jid'=. scanpair /: (0,,scanpair_index{.close) i. scanpair
> > > >         kori=. }.(({."1 known) i.kid){known
> > > >         known=: known,jid,t=.relori (kori reorient kid{::scans); 
> > > > jid{::scans
> > > >    end.
> > > >    #~.>,each/(}."1/:~~.known) reorient L:0"_1 scans
> > > > }}
> > > >
> > > > I've left a variety of global assignments here, because looking at
> > > > example data really helps when understanding the code. Also
> > > >
> > > > For part B, the problem was to find the largest "manhattan distance"
> > > > between two scanners.
> > > >
> > > > In other words, given the location of each scanner
> > > >     2}."1~.known
> > > >
> > > > take the sum of the absolute values of the difference between each
> > > > pair of locations, then find the largest of those sums
> > > >
> > > > b19=:{{
> > > >    a19 y
> > > >    >./,+/"1|-"1/~2}."1~.known
> > > > }}
> > > >
> > > > If there's something here which I should have explained better, and
> > > > you can think of a way to phrase a question about it, let me know...
> > > >
> > > > Thanks,
> > > >
> > >
> > >
> > > --
> > > This email has been checked for viruses by Avast antivirus software.
> > > https://www.avast.com/antivirus
> > >
> > > ----------------------------------------------------------------------
> > > For information about J forums see http://www.jsoftware.com/forums.htm
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to