Words crossing over

21

1

Input:

Two strings (NOTE: the order of input is important).

Output:

Both words/sentences start on lines with one empty line in between them. They 'walk' horizontally 'next to each other'. But when they have the same character at the same position, they cross each other, and then continue walking 'next to each other'.

Confusing you say? Let's give an example:

Input: Words crossing over & Ducks quacking:

Word  quack n 
    s      i g
Duck  cross n  over

As you can see, these are the paths:
Excuse the bad MS paint..

Challenge rules:

  • We always first go back walking a straight line after we've 'crossed over' before we can cross over again (see test case above {1} - where ing is equal, but after we've crossed on i, we first have to go back walking straight (thus ignoring n), before we can cross again on g).
  • The inputs can be of different length, in which case the longer one continues walking in a straight line (see test cases 1, 2, 4 & 6).
  • Both inputs can be the same (see test case 3).
  • The inputs won't contain any tabs nor new-lines.
  • Spaces are ignored as characters that are the same (as an edge case), in which case the next (non-space) character after that - if any - is crossing over instead (see test cases 3, 5 & 6).
  • The inputs can have no adjacent (non-space) characters on the same position at all, in which case both just walk in a straight line horizontally (see test cases 2).
  • Even if the first character is equal, we always start two lines apart (see test cases 3 & 6).
  • Trailing spaces and a single trailing new-line are optional.
  • You can assume the inputs will only contain printable ASCII characters (new-lines and tabs excluded).
  • The inputs are case-sensitive, so A and a aren't equal, and won't cross over (see test case 7).
  • Both the inputs lengths will always be at least 2.
  • Input & output can be in any reasonable format. Can be a single String with new-lines; a String-array/list; printed to STDOUT; 2D array of characters; etc.

General rules:

  • This is , so shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters and return-type, full programs. Your call.
  • Default Loopholes are forbidden.
  • If possible, please add a link with a test for your code.
  • Also, please add an explanation if necessary.

Test cases:

1. Input: "Words crossing over" & "Ducks quacking"

1. Output:
Word  quack n 
    s      i g
Duck  cross n  over

2. Input: "bananas" & "ananas"

2. Output:
bananas

ananas

3. Input: "I see my twin!" & "I see my twin!"

3. Output:
I  e   y  w n 
  s e m  t i !
I  e   y  w n 

4. Input: "Is the weather nice?" & "Not really, no.."

4. Output:
Is th ally, no..
     e
Not r  weather nice?

5. Input: "Codegolf & Programming Puzzles" & "The golfer might solve puzzles"

5. Output:
Code o f & Programming P z l s
    g l                 u z e
The  o fer might solve p z l s

6. Input: "Can you turn the lights off?" & "Can you try to solve this?"

6. Output:
C n  o   urn the  ve  s off?
 a  y u t        l   t 
C n  o   ry to so igh his?

7. Input: "one Ampere" & "two apples"

7. Output:
one Am les
      p
two ap ere

8. Input: "Words crossing" & "Words Quacking"

8. Output:
W r s cross n 
 o d       i g
W r s Quack n 

Kevin Cruijssen

Posted 2017-08-11T08:54:13.960

Reputation: 67 575

Answers

4

Japt, 56 47 33 bytes

y ®m+S éBv ©ZꬩZx ?°B:B=c2)¯3÷y

Test it online! Takes input as an array of two strings.

I am a total moron... y ® is a million times easier to use on two different-length strings than U¬íV¬@...

Explanation

y ®   m+S éBv © Zê¬ © Zx ?° B:B= c2)¯  3à ·  y
y mZ{Zm+S éBv &&Zêq &&Zx ?++B:B=Bc2)s0,3} qR y

              Implicit: U = array of two strings
y             Transpose U, padding the shorter string with spaces in the process.
mZ{        }  Map each pair of chars Z by this function: (we'll call the chars X and Y)
  Zm+S          Append a space to each char, giving X + " " + Y + " ".
  Bv            If B is divisible by 2
  &&Zêq           and Z is a palindrome (X and Y are the same)
  &&Zx ?          and Z.trim() is not empty (X and Y are not spaces):
    ++B           Increment B. B is now odd; the top and bottom strings are swapping.
  :             Otherwise:
    B=Bc2         Ceiling B to a multiple of 2. (0 -> 0, 1 -> 2, 2 -> 2, etc.)
  é       )     Rotate the string generated earlier this many chars to the right.
  s0,3          Take only the first 3 chars of the result.
qR            Join the resulting array of strings with newlines.
y             Transpose rows with columns.
              Implicit: output result of last expression

B is a variable that keeps track of which state we're in:

  • B % 4 == 0 means first word on top, but ready to switch;
  • B % 4 == 1 means we've just switched;
  • B % 4 == 2 means second word on top, but ready to switch;
  • B % 4 == 3 means we've just switched back.

B happens to be preset to 11; since 11 % 4 == 3, the first column always has the first word on top. We increment B any time the words swap positions, or any time it's odd (with B=c2).

ETHproductions

Posted 2017-08-11T08:54:13.960

Reputation: 47 880

6

APL (Dyalog), 64 bytes

{C←⎕UCS⋄1↓e⊖' '⍪1 0 1⍀⍵⊖⍨≠\e←C(2/⊃l)⎕R(l←C⌽⍳2)C(0@0=⌿⍵)∧' '≠1⌷⍵}

Try it online!

Adám

Posted 2017-08-11T08:54:13.960

Reputation: 37 779

If the strings start with 3 identical letters, this intersects the third letters, not the second ones. I am unsure if this is the correct result, I have asked the OP. – Mr. Xcoder – 2017-08-11T09:39:10.167

@Mr.Xcoder Thanks. Should be fixed now. – Adám – 2017-08-11T09:44:59.587

Ok, nice solution then. If you have time, maybe you can add an explanation :P – Mr. Xcoder – 2017-08-11T09:47:48.600

@Mr.Xcoder Yes, I always do. (Ping me if you see any unexplained answer of mine!) – Adám – 2017-08-11T09:48:28.933

That's why I am always delighted to see your answers :) - Because I understand your submissions more than I understand mine. – Mr. Xcoder – 2017-08-11T09:49:25.100

@Mr.Xcoder Thank you. I appreciate your words. – Adám – 2017-08-11T09:50:14.557

1

@Adám Yeah surely...or maybe? Might have something to do with this as well...oh and an unexplained answer! Or maybe two...? And something I don't quite get.

– Erik the Outgolfer – 2017-08-11T09:51:55.107

@EriktheOutgolfer Thanks. Done. – Adám – 2017-08-11T09:53:53.020

@EriktheOutgolfer All done. Thanks. – Adám – 2017-08-11T11:10:28.353

4

Charcoal, 69 bytes

AE⮌θιθAE⮌ηιηW∧θη«A⊟θεA⊟ηδA∧¬∨φ⁼ε ⁼εδφ¿φ«εAθδAηθAδη»«↑↓ε↓↗δ»»¿θ↑↓↑⮌⁺θη

Try it online! Link is to verbose version of code. Explanation:

AE⮌θιθAE⮌ηιη        Turn the input strings into arrays and reverse them
W∧θη«               While both valus still have characters left
     A⊟θεA⊟ηδ       Extract the next pair of characters
     A∧¬∨φ⁼ε ⁼εδφ   Determine whether this is a crossing point
     ¿φ«εAθδAηθAδη  If so then print the character and switch the value
      »«↑↓ε↓↗δ»»     Otherwise print the two characters apart
¿θ↑↓                Move to print any remaining characters accordingly
↑⮌⁺θη               Print any remaining characters

Neil

Posted 2017-08-11T08:54:13.960

Reputation: 95 035

3

Python 2, 217 210 bytes

-1 byte thanks to officialaimm

a,b=map(list,input())
n=max(len(a),len(b))
c=[' ']*n
a=(a+c)[:n]
b=(b+c)[:n]
for i in range(1,n):
 if a[i]==b[i]!=' '==c[i-1]:c[i]=a[i];a[i]=b[i]=' ';a[i:],b[i:]=b[i:],a[i:]
print'\n'.join(map(''.join,[a,c,b]))

Try it online!

Rod

Posted 2017-08-11T08:54:13.960

Reputation: 17 588

11 byte by predefining s=' ' – officialaimm – 2017-08-11T11:57:02.690

1@officialaimm I did some changes, now it have the same byte count =/ – Rod – 2017-08-11T11:58:57.740

2

Haskell, 142 138 bytes

g(a:b)f(c:d)|f>0,a==c,a>' '=[' ',a,' ']:g d 0b|1<2=[a,' ',c]:g b 1d
g[]_[]=[]
g b f d=g(max" "b)f$max" "d
a&b=[[j!!i|j<-g a 0b]|i<-[0..2]]

Try it online!

How it works:

g                    -- function g constructs a list of lists of three characters
                     --   the 1st char belongs to the upper line,
                     --   the 2nd char to the middle line and
                     --   the 3rd char to the lower line
      f              -- flag f indicates if crossing is allowed or not
 (a:b) (c:d)         -- strings to cross
  |f>0               -- if crossing is allowed
      ,a==c          -- and both strings start with the same char
           ,a>' '    --   that is not a space
   =[' ',a,' ']      -- return space for upper/lower line and char a for the middle line
      :g d 0b        -- and go on with crossing disabled and strings swapped
 |1<2=               -- else
   [a,' ',c]         -- keep chars in their lines and
      :g b 1d        --  go on with crossing enabled

g[]_[]=[]            -- base case: stop when both strings are empty

g b f d=             -- if exactly one string runs out of characters
 g(max" "b)f$max" "d --   replace it with a single space and retry

a&b=                 -- main function
          i<-[0..2]  -- for each line i from [0,1,2]    
       j<-g a 0b     -- walk through the result of a call to g with crossing disabled
    j!!i             -- and pick the char for the current line  

nimi

Posted 2017-08-11T08:54:13.960

Reputation: 34 639

+1 nice answer. It does seem to have one little bug however, because it now starts crossing when the first two characters are equal, like in tests 3, 6 and 8 (TIO). Also, I think you forgot a word in your explanation sentence "return space for upper/lower line and a ¿¿¿ for the middle line".

– Kevin Cruijssen – 2017-08-11T14:03:49.800

1@KevinCruijssen: Thanks for finding the bug. Luckily it's easy to fix: just start with g 0. Regarding the missing word: "a" as in "variable named a", but that's indeed confusing, so I reworded it. – nimi – 2017-08-11T14:16:07.167

Ah, that a. :) I personally use a in my explanations when indicating to the variables, although usually it's clear enough without. Thanks for the clarification, and I was indeed expecting a pretty easy bug-fix for it. – Kevin Cruijssen – 2017-08-11T14:25:04.767

Not sure how to do 'a' (with ' being replaced with a back-tick) in a comment anymore, which is what I wanted to say.. (So I use back-ticks around variables, inside my code-blocks.) – Kevin Cruijssen – 2017-08-11T14:37:41.843

2

JavaScript (ES6), 112 bytes

(a,b,c='',g=([a,...A],[b,...B],w)=>a?w&a==b&a>' '?' '+g(B,A,c+=a):a+g(A,B,1,c+=' '):'')=>g(a,b)+`
`+c+`
`+g(b,a)

Ungolfed:

f=
(a,b,                                    //the inputs
 c='',                                   //c will hold the middle sentence
 g=([a,...A],[b,...B],w)=>               //define a function to walk through the strings
                                         //w will be false if we're at the beginning,
                                         //... or if we've just done a swap
     a?                                  //are there any letters left?
       w&a==b&a>' '?' '+g(B,A,c+=a):     //if we haven't just swapped and the letters match,
                                         //... add the current letter to c 
                                         //... and recurse swapping the strings
                    a+g(A,B,1,c+=' '):   //else add a space to c and continue processing
                    ''
)=>
g(a,b)+'\n'+                             //call g with a, b
c+'\n'+                                  //output c
g(b,a)                                   //call g with b, a

Test cases:

f=
(a,b,c='',g=([a,...A],[b,...B],w)=>a?w&a==b&a>' '?' '+g(B,A,c+=a):a+g(A,B,1,c+=' '):'')=>g(a,b)+`
`+c+`
`+g(b,a)

console.log(f('Words crossing over','Ducks quacking'));
console.log('____________');
console.log(f('bananas','ananas'));
console.log('____________');
console.log(f('I see my twin!', 'I see my twin!'));
console.log('____________');
console.log(f('Is the weather nice?', 'Not really, no..'));
console.log('____________');
console.log(f('Codegolf & Programming Puzzles', 'The golfer might solve puzzles'));
console.log('____________');
console.log(f('Can you turn the lights off?', 'Can you try to solve this?'));
console.log('____________');
console.log(f('one Ampere', 'two apples'))
console.log('____________');
console.log(f('Words crossing','Words Quacking'));
console.log('____________');

Rick Hitchcock

Posted 2017-08-11T08:54:13.960

Reputation: 2 461

1

Perl 5, 211 bytes

@a=map[/./g],<>;$b=1;($f,@{$r[$i]})=$a[0][$i]eq$a[1][$i]&&$f&&$a[0][$i]ne$"?(0,$",$a[0][$i],$",$t=$b++):(1,$a[$t%2][$i],$",$a[$b%2][$i]),$i++while$a[0][$i]||$a[1][$i];for$i(0..2){print$r[$_][$i]for 0..$#r;say''}

Try it online!

# Perl 5, 234 bytes

fixed the bug Kevin pointed out

@a=map[/./g],<>;$l=@{$a[0]}>@{$a[1]}?@{$a[0]}:@{$a[1]};$b=1;@{$r[$_]}=$a[0][$_]eq$a[1][$_]&&$_&&$r[$_-1][1]eq$"&&$a[0][$_]ne$"?($",$a[0][$_],$",$t=$b++):($a[$t%2][$_],$",$a[$b%2][$_])for 0..$l;for$i(0..2){print$r[$_][$i]for 0..$l;say}

Try it online!

Xcali

Posted 2017-08-11T08:54:13.960

Reputation: 7 671

Hi, when I try to test the test case "Can you turn the lights off?" & "Can you try to solve this?" I seem to be getting an error: Modification of non-creatable array value attempted, subscript -1 at .code.tio line 1, <> line 2. Is this a bug, or am I doing something incorrect? Here is the TIO.

– Kevin Cruijssen – 2017-08-11T16:48:03.893

1Bug. When the first two characters were the same, an array subscript was -1, which is only valid if there is data in the array. Fixed it with 4 more bytes. – Xcali – 2017-08-11T17:30:28.417

1

APL (Dyalog), 50 bytes

{3↑(0,+\2∨/2|{⍵⌈a×1+1,¯1↓⍵}⍣≡a←>⌿2=⌿3↑⍵)⊖⍵⍀⍨¯1*⍳4}

Try it online!

⍵⍀⍨¯1*⍳4 gives the matrix:

Words.crossing.over
...................
Ducks.quacking.....
...................

(the dots represent spaces). Its columns will be rotated by different amounts so the first three rows end up looking like the desired result - hence the 3↑ near the beginning. The rest of the algorithm computes the rotation amounts.

Within the parens: 3↑⍵ creates a matrix like

Words.crossing.over
Ducks.quacking.....
...................

and 2=⌿ compares its rows pairwise, i.e. first string vs second string and second string vs the all-spaces row.

0 0 0 0 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1

We are interested in where the former is true (1) and the latter false (0), so we reduce with >⌿ to get a boolean vector named a.

0 0 0 0 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0

Now, in every stretch of 1-s we need to zero out the even occurrences because no two twists can occur next to each other. First we obtain a numbering like:

0 0 0 0 1 0 0 0 0 0 0 1 2 3 0 0 0 0 0

by, loosely speaking, replacing a[i] with a[i]*max(a[i-1]+1, a[i]) until the result stabilises: {⍵⌈a×1+1,¯1↓⍵}⍣≡, and we take that mod 2: 2|

0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0

Now we know where the twists will occur. We copy each 1 to the left - 2∨/ (pairwise "or"):

0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0

and compute the partial sums - +\

0 0 0 0 1 2 2 2 2 2 2 3 4 5 6 6 6 6 6

That gives us the column rotation amounts we needed in the beginning. Modulo 4 is implied.

ngn

Posted 2017-08-11T08:54:13.960

Reputation: 11 449

Nice! Even 14 bytes shorter than Adám. Could you add an explanation (I'm sure you are making it as we speak, but in case you aren't.. ;) – Kevin Cruijssen – 2017-08-12T14:52:00.317

Explanations only take away the pleasure of figuring out how it works on your own... :) – ngn – 2017-08-12T16:04:06.240

0

05AB1E, 31 bytes

ζεËNĀ¾Èyðå_Pi¼ë¾É½}yð«S¾._¨}øJ»

Port of @ETHproductions's Japt answer, but with two minor differences:
1) I take the input as a 2D list of characters instead of list of strings.
2) The counter_variable in 05AB1E is 0 by default, instead of 11 (or 3) like the B in Japt, so the is added as additional check inside the map (and I rotate towards the right instead of left).

Try it online or verify all test cases.

Explanation:

ζ                  # Zip/transpose (swapping rows/columns) the (implicit) input-list
                   # with space filler by default to create pairs
 ε          }      # Map each pair `y` to:
  Ë                #  Check if both values in the pair are equal
  NĀ               #  Check if the map-index is not 0
  ¾È               #  Check if the counter_variable is even
  yðå_             #  Check if the pair contains no spaces " "
  Pi               #  If all checks are truthy:
    ¼              #   Increase the counter_variable by 1:
   ë               #  Else:
    ¾É             #   Check if the counter_variable is odd
      ½            #   And if it is: increase the counter_variable by 1
   }               #  Close the if-else
    yð«            #  Add a space after both characters in the pair
       S           #  Convert it to a list of characters (implicitly flattens)
        ¾._        #  Rotate this list the counter_variable amount of times towards the right
           ¨       #  And then remove the last character
             ø     # Zip/transpose; swapping rows/columns
              J    # Join each inner character-list to a single string
               »   # Join everything by newlines (and output implicitly)

Kevin Cruijssen

Posted 2017-08-11T08:54:13.960

Reputation: 67 575