13
\$\begingroup\$

Background Information

Inspired by this answer on Arqade SE

Minecraft has rails which allow you to move minecarts around on them for transporting players, entities, and items. There are many types of rails, but today we're only interested in regular rails, which are also the only type that can curve.

When you place down a normal rail that has two adjacent rails, it will connect them. If they are not on opposite sides of the rail, the rail will curve to a 90 degree angle. However, if there are more than two adjacent rails, it can't connect all of them. In that case, it will always favor going south-east (this is called the south-east or Z-X rule) (for this challenge, north is up, east is right, south is down, west is left). If there is a T-junction, the rail will always connect to the "vertical" branch of the T, and then it will prioritize south over north if the main branch is east-west, and east over west if the main branch is north-south. If there is a four-way junction, the rail will connect south and east.

I/O

For this challenge, you will need eight distinct characters: empty space, vertical rail, horizontal rail, up-left curved rail, up-right curved rail, down-left curved rail, down-right curved rail, and placeholder. You may select any characters; I choose to use box-drawing characters in my examples. You may count these eight characters as one byte in your code (this is just to allow people to use box-drawing characters instead of just like " |-WXYZ#" or something not as nice-looking).

You will be given input in a convenient format for a layout of existing rails. This will be a grid, so you may take it as a matrix of characters, a list of strings, newline-separated strings, etc. Anything reasonable. To simplify a bit, your input will only contain straight rails, placeholders, and blanks.

Your output should be a grid of the same size, but in each placeholder you are to place a rail according to the rules given. To make this challenge a bit simpler, every placeholder will always have at least two adjacent rails that are already facing the appropriate direction, so you should not change any cells except the placeholders. Note that it is possible for the rails to be on opposite sides of the placeholder, in which case you may need to place down a straight rail.

Detailed Example

This contains every possible configuration:

.═.═.═.
║ ═ ║ ║
.║  ║ ║
║   ║ ║
.═══.═.
║   ║ ║
.═══.═.

The output for this would be:

╔═══╔═╗
║ ═ ║ ║
║║  ║ ║
║   ║ ║
╔═══╔═╗
║   ║ ║
╚═══╚═╝

Note that it's valid to have rails facing the wrong direction next to placeholders. You are guaranteed that at least two adjacent rails are facing the right way though. Technically, in Minecraft, this might cause the side rails to turn to face the new rail and then curve, but in this specific challenge, we'll just assume that existing straight rails will not turn, and you just need to connect up exactly two of the adjacent, correctly-oriented rails in accordance to the S-E rule.

   .══.══.             ╔═════╗
   ║     ║             ║     ║
.══.══.  .          ╔══╔══╗  ║
║  ║  ║  ║    ->    ║  ║  ║  ║
.══.══.  .          ╚══╔══╝  ║
   ║     ║             ║     ║
   .══.══.             ╚═════╝

You're running out of space on your minecart to write your code, so your code must be as short as possible! This is , so the shortest answer in bytes wins (remember that you may count your eight I/O characters as one byte).

\$\endgroup\$
3
  • \$\begingroup\$ Can you add some more test cases? \$\endgroup\$
    – Jonah
    May 1 at 22:00
  • 1
    \$\begingroup\$ @Jonah Added another; I'll add more if I think of any more edge cases that are important to cover. \$\endgroup\$
    – hyper-neutrino
    May 1 at 22:10
  • 2
    \$\begingroup\$ someone should implement this in ruby on rails \$\endgroup\$
    – Razetime
    May 3 at 2:47
4
\$\begingroup\$

Charcoal, 62 bytes

≔⪪##╗#║╝╗#╔═╔╚╔╚╔¹ηWS⊞υι≔⪫υ⸿θPθFθ¿⁼.ι§η⊖⍘⭆⪪⭆KV﹪÷⊕⌕ηκX²λ²¦²⮌κ²ι

Try it online! Link is to verbose version of code. Byte count is adjusted for deverbosifier bug and box drawing characters. Explanation:

≔⪪##╗#║╝╗#╔═╔╚╔╚╔¹η

Create a table of box-drawing characters which serves two purposes: 1) to look up to see whether an adjacent box-drawing character wants to connect 2) (after some bit twiddling) to look up which box-drawing character to place.

WS⊞υι≔⪫υ⸿θPθ

Read the grid and write it to the canvas without moving the cursor.

Fθ

Loop over the grid again.

¿⁼.ι

If this is a placeholder, then...

§η⊖⍘⭆⪪⭆KV﹪÷⊕⌕ηκX²λ²¦²⮌κ²

... overwrite it with the result of looking up the adjacencies of connecting box-drawing characters in the table. The characters are checked in the order above, right, below, left, so the table is designed so that characters that join from above are in odd 1-indexed positions in the table, while characters that join from the left are in the right half of the table. We then need to find the character that connects above, right, below or left as appropriate, which is normally a rotation of the bits by 2, but because of the way base conversion works in Charcoal we end up reversing the bits, which means that we need to swap each pair of bits to end up with the correct output character.

ι

Otherwise just overwrite the character with itself.

Example box-drawing character lookup:

╔═╗
╚.╝
 ║
  • The character above is in position 10, which does not have bit 0 set, so the resulting first bit is 0
  • The character to the right is in position 6, which does have bit 1 set, so the resulting second bit is 1
  • The character below is in position 5, which does have bit 4 set, so the resulting third bit is 1
  • The character to the left is first seen in position 12, which does have bit 3 set, so the resulting fourth bit is 1
  • The first and last pairs of bits are then exchanged resulting in 1011
  • The eleventh character in the table is so this is the resulting output.
\$\endgroup\$
4
\$\begingroup\$

Python 3, 394 378 370 328 bytes

def p(s,i,R,C,H='═',V='║'):
 v=i%C and s[i-1]==H
 v|=(i//C and s[i-C]==V)<<3
 v|=(i%C-C+1and s[i+1]==H)<<2
 return' ═║╗══╔╔║╝║╗╚╚╔╔'[v|(i//C-R+1and s[i+C]==V)<<1]
def f(s,i=0):
 t=s.split('\n');R,C,s=len(t),len(t[0]),''.join(t)
 for c in s:print([c,p(s,i,R,C)][c=='.'],end='\n'[:-~i%C==0]);i+=1

Try it online!

A very naive method. Receives as input a string and iterates over its characters. If a character is a point (a placeholder), replace it with the character returned by p.

The function p builds a "4-bits integer" v, where:

  • if its 1st bit is set, the placeholder has a valid connection with the west;
  • if its 2nd bit is set, the placeholder has a valid connection with the south;
  • if its 3rd bit is set, the placeholder has a valid connection with the east; and
  • if its 4th bit is set, the placeholder has a valid connection with the north.

A invalid connection occurs with a direction when

  1. the rail is on a border of that direction (the first placeholder in the example given by the OP has an invalid connection with the north and the west since it's in the border, so its 4-bit integer is 1010).
  1. the rail of that direction is misaligned with the placeholder (the fifth placeholder in the example given by the OP has an invalid connection with the east since the east rail is misaligned, so its 4-bit integer is 1011).

After this 4-bit integer is built, it returns the corresponding box-drawing character using this integer as index of a string.

\$\endgroup\$
3
  • \$\begingroup\$ This case seems to fail; can you check if something is wrong or if I've inputted the test case incorrectly or something? \$\endgroup\$
    – hyper-neutrino
    May 1 at 22:07
  • \$\begingroup\$ You're right, I've missed some configurations in the map, I'll updade it. \$\endgroup\$
    – enzo
    May 1 at 22:38
  • \$\begingroup\$ @hyper-neutrino I think it's fixed now, I've updated so it now covers all configurations (from 0000 to 1111) using a string instead of a dictionary, if you find another bug please let me know :-) \$\endgroup\$
    – enzo
    May 1 at 22:48
2
\$\begingroup\$

JavaScript (ES6), 144 bytes

Expects and returns a matrix of characters. Uses 01234567 instead of ═║.╔╗╚╝.

m=>m.map((r,y)=>r.map((v,x)=>v-3?v:(a=r[x-1]==1,b=r[x+1]==1,c=y&&m[y-1][x]>1,d=(m[y+1]||0)[x]>1,a+b+c+d>2?(a=a*b?0:a,c*d?0:c):c)*6+a+b*8+d*4&7))

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.