| Bytes | Lang | Time | Link |
|---|---|---|---|
| 1825 | Vyxal | 240320T124248Z | lyxal |
| 021 | 05AB1E | 240411T103132Z | Kevin Cr |
| 167 | Python | 240319T090756Z | Nicola S |
| 130 | Python | 240320T141342Z | lynn |
| 036 | Uiua | 240319T113058Z | noodle p |
| 287 | R 4.3.2 | 240318T140441Z | int 21h |
| 040 | APL Dyalog Unicode | 240318T164618Z | akamayu |
| 181 | Matlab | 240319T004335Z | Xenox |
| 093 | Perl 5 + pF | 240318T124818Z | Dom Hast |
| nan | APL+WIN | 240318T162207Z | Graham |
| 151 | JavaScript Node.js | 240318T135847Z | Arnauld |
| 062 | J | 240319T032021Z | Jonah |
| 023 | Jelly | 240318T191317Z | Jonathan |
| 028 | Vyxal | 240318T193122Z | math sca |
| 025 | Charcoal | 240318T183338Z | Neil |
| 053 | K ngn/k | 240318T153351Z | ovs |
| 164 | JavaScript Node.js | 240318T094014Z | l4m2 |
Vyxal, 146 bitsv2, 18.25 bytes
RƛṫøA‹$ṫ"$꘍kzJ;⁽Ṙẇf§
Bitstring:
01100011010100001110000111101010000100100111011100100011100110011010010111100111000011110101010100011001011010111000101101001001001101011011110010
20 bytes unencoded. Thanks to @emanresu for the golfing suggestion which I somehow didn't notice for 7 months.
First code golf in a while. Feels good to be back doing the golf.
Explained (old)
ƛḣ$øA‹$ḣṘ"$꘍;⁽ṘẆnṘ:"j§
ƛ ; # To each string S in the input:
ḣ$ # Push S[1:], S[0]
øA‹ # and calculate the 0 based index of S[0] in the alphabet
$ḣṘ # Push S[1:][0], S[1:][1::-1]
" # and pair into a list. The first item is the bit that's coming out of the stem, the second is the spike.
$꘍ # and prepend (number of spaces calculated earlier) to each string in that pair
# This generates a list of what'll be in each column. The spaces at the front are how far down the cactus the characters will be.
⁽ṘẆ # Reverse the order of the first item so it's [spike, stem]
nṘ:" # ["zyx...cba", "zyx...cba"]
j # Join the list of lists of stem and spike on ^. By some miracle, this also flattens the list.
§ # Transpose, and join on newlines
💎
Created with the help of Luminespire.
05AB1E, 21 bytes
εćUć‚AXkúAª}`R«€SζJ»R
Inputs as a pair in reversed order.
Explanation:
ε # Map over both strings:
ć # Extract head; pop and push remainder-string and first character
U # Pop this first character and store it in variable `X`
ć # Extract head again
‚ # Pair the remainder-string and first char together
AXk # Get the 0-based index of character `X` in the lowercase alphabet
ú # Pad that many leading spaces to both strings in the pair
Aª # Append a lowercase alphabet to this pair
}` # After the map: pop and push both triplets separated to the stack
R # Reverse the second/top triplet
« # Merge the two triplets together
€S # Convert each string to a list of characters
ζ # Zip/transpose; swapping rows/columns,
# with space as filler character for unequal length rows
J # Join all inner character lists back together to strings
» # Join these strings by newlines
R # Reverse the entire multiline string
# (after which the result is output implicitly)
I originally started with an approach that uses the Canvas builtin to draw everything at once, but this ended up roughly three times as long (57 bytes):
Aû¹н©¡¨®ý¹ð'aAûIн©¡¨®ýIJ₂₂A¹нk-3¹gÍ0Y3₂₂®-3²gÍ)ć•ĀçR5•SRΛ
Python, 167 bytes
lambda l,r,o=lambda s:" "*(ord(s[0])-97):[("{:99s}"*6).format(o(l)+l[2:],o(l)+l[1],*["abcdefghijklmnopqrstuvwxyz"]*2,o(r)+r[1],o(r)+r[2:])[98-i::99]for i in range(99)]
- Returns a list of rows
- Inputs that would require more than 99 chars won't work, which is allowed
- Expects 2 words, lowercase ascii (or at least their first char must be), length of 2 or more (it could be 1 byte shorter if I defaulted to uppercase)
Old version (function with print rather than lambda: I wasn't too sure about I/O conventions) with explanation:
Python, 197 194 192 185 178 175 174 bytes
o=lambda s:" "*(ord(s[0])-97)
def f(l,r):[print(("{:99s}"*6).format(o(l)+l[2:],o(l)+l[1],*["abcdefghijklmnopqrstuvwxyz"]*2,o(r)+r[1],o(r)+r[2:])[98-i::99])for i in range(99)]
- Prints a cactus to STDOUT, vertically padded to 99 chars
- Inputs that would require more than 99 chars won't work, which is allowed
- Expects 2 words, lowercase ascii (or at least their first char must be), length of 2 or more.
- (It could be 1 byte shorter if I defaulted to uppercase)
How?
o=lambda s:" "*(ord(s[0])-97) # Function that, given an input word,
# returns as many whitespaces as the
# index of the first char in unicode,
# after "a" (ord("a") == 97), i.e.
# how much the branch should be raised
# from the ground.
def f(l,r):
# Cactus built horizontally by rows:
( o(l)+l[2:], # * padding + letters 3+ of the word
o(l)+l[1], # * padding + second letter
*["abcdefghijklmnopqrstuvwxyz"]*2, # * whole alphabet twice (ugly but I
# can't golf it further as strings)
o(r)+r[1], # * padding + second (right)
o(r)+r[2:] ) # * padding + letters 3+ (right)
#
# └──────┬──────┘
# └──────────────┐
# ╽
("{:99s}"*6).format( ... ) # Concatenation of 6 strings, each
# # right-padded to 99 characters.
# └───────────┬────────────┘
# ┌────┘
# ╽
[print( ... [98-i::99])for i in range(99)] # A way of wrapping the string at 99
# and printing it transposed.
```
Python, 130 bytes
lambda a,b:[*zip(*g(a),*g(b)[::-1])][::-1]
s=" "*102
g=lambda a:[(p:=s[65:ord(a[0])])+a[2:]+s,p+a[1]+s,"%c"*26%(*range(65,91),)+s]
A lambda returning a matrix (list of tuples) of characters.
Uiua, 40 38 36 bytes
⇌⍉⬚@ (⊂⇌∩(⊂+@a⇡26≡⊂:⊟°⊂:¤↯:@ -@a°⊂))
38 → 36 bytes thanks to Joao-3 :)
Explanation
⬚@ (…)
Uiua won’t join together arrays of different sizes, so this sets a fill value of a space for the following function:
-
∩(…)Call this function on both inputs:
-
¤↯:@ -@a°⊂Separate the first character from the rest, get the character's position in the alphabet and make a string of that many spaces, then fix it (wrap it in an array) for later.
-
⊂+@a⇡26≡⊂:⊟°⊂:Separate the first character of the rest from the rest, and use them both to make an arm. Then join with the spaces made earlier, get the alphabet and join it to the 2D matrix.
-
-
⊂⇌Reverse the first arm and join it to the second.
⇌⍉
Rotate the array to the correct orientation, and it’s done!
R 4.3.2, 287 bytes
Certainly, there's room for optimization...
Edit
THE final version.
Bonus: the headspace region is calculated to fit exactly the height of the cactus including the branches
f=\(x,y){a=rev(letters);s=substr;b=nchar;`!`=\(c)which(a==s(c,1,1));`?`=\(y)((24+b(y)-!y)%/%26)*(b(y)-2-!y);k=max(?x,?y);r=l=m=q=rep(" ",26+k);a=c(rep(" ",k),a);`?`=\(e)s(e,2,2);`/`=\(l,x){for(j in 3:b(x))l[3-j+!x]=s(x,j,j);l};m[!x]=?x;q[!y]=?x;l=l/x;r=r/y;writeLines(paste0(l,m,a,a,q,r))}Try it online! (backslash replaced with
functiondue to the old R version)
I have tried to replace writeLines with cat("\n"), that removed 2 bytes of the code, but the output is slightly different (an empty line over the cactus):
g=\(x,y){a=rev(letters);s=substr;b=nchar;`!`=\(c)which(a==s(c,1,1));`?`=\(y)((24+b(y)-!y)%/%26)*(b(y)-2-!y);k=max(?x,?y);r=l=m=q=rep(" ",26+k);a=c(rep(" ",k),a);`?`=\(e)s(e,2,2);`/`=\(l,x){for(j in 3:b(x))l[3-j+!x]=s(x,j,j);l};m[!x]=?x;q[!y]=?x;l=l/x;r=r/y;cat(paste0("\n",l,m,a,a,q,r))}
Yet another R, 231 bytes
This is a stripped down version - without the cactus headspace calculation. The height of output window is 99 lines.
\(x,y){a=rev(letters);s=substr;b=nchar;`!`=\(c)which(a==s(c,1,1));r=l=m=q=rep(" ",99);a=c(rep(" ",73),a);`?`=\(e)s(e,2,2);`/`=\(l,x){for(j in 3:b(x))l[3-j+!x]=s(x,j,j);l};m[!x]=?x;q[!y]=?x;l=l/x;r=r/y;cat(paste0("\n",l,m,a,a,q,r))}
Test (the first method):
> f("yoghurt","antidisestablishmentarianism")
t
r
u
h zz m
goyy s
xx i
ww n
vv a
uu i
tt r
ss a
rr t
qq n
pp e
oo m
nn h
mm s
ll i
kk l
jj b
ii a
hh t
gg s
ff e
ee s
dd i
cc d
bb i
aant
APL (Dyalog Unicode), 51 49 44 42 40 bytes SBCS
Assume ⎕io←0, take input as left and right arguments.
44 -> 42 thanks to @ovs, and 42 -> 40 thanks to @att.
⊖⍉∘↑⍤,∘⌽⍥{((a⍳⊃⍵)↓⍣¯1¨⌽0 1 1⊂⍵),⊂a←⎕C⎕A}
How?
The dfn transforms a string into its half of the cactus.
{((a⍳⊃⍵)↓⍣¯1¨⌽0 1 1⊂⍵),⊂a←⎕C⎕A}
a←⎕C⎕A a is abcd...z, half of the middle stem.
⌽0 1 1⊂⍵ vertical and horizontal stems that are
↓⍣¯1¨ aligned (using white spaces) with
(a⍳⊃⍵) the first letter of ⍵ in a
With the dfn denoted as f, the answer is ⊖⍉∘↑⍤,∘⌽⍥f which is equivalent to {⊖⍉↑(f⍺),(⌽f⍵)}.
{⊖⍉↑(f⍺),(⌽f⍵)}
(f⍺) (⌽f⍵) two halves of the cactus
, stick them together
⊖⍉↑ combine the columns
Matlab, 181 bytes
Try it online! Input for first test case would be F(["cartoonish";"testing"])
I feel like Matlab isn't well suited for working with strings. But it was a great exercise in array indexing.
s=char(cellstr(input("")));C(1:26,3:4)=char([97:122;97:122]');for i=1:2;C(s(i,1)-96,5:6)=s(i,2:3);C(s(i,1)-95:s(i,1)+length(s(i,:))-99,6)=s(i,4:end)';C=flip(C,2);end;flip(flip(C),2)
Perl 5 + -pF, 93 bytes
This solution utilises ANSI escape sequences and necessitates a compatible terminal emulator. This solution uses a hard-coded row position of 29, but this can be trivially changed to increase the available space (at the cost of having to scroll to see the cactus) up to 99 without incurring any additional byte-cost. There is another version below that scales as needed.
$_=".[29;".(3+$|--).H.join$"="..[A",a..z;s/$F[0]\K/.[s@{[$|?"..$F[1]..":$F[1]]}@F[2..@F].[u/g
Explanation
For each input string, the input is automatically read, placed in $_ (implicit -n from -p) and split into characters in @F (-F).
$_ is then re-set to "\x1b[29;nH" (where n is either 3 or 4 depending on the alternating value of $|) which positions the cursor on the 29th row and 3rd (or 4th) column, and has the lowercase alphabet - joined on "\x08\x1b[A" which is backspace followed by the ANSI escape sequence for "up one row" (which is also stored in $") - concatenated to it.
s/// works on $_ by default, so the first char of the word ($F[0]) is s///ubstituted (keeping the start letter in place using \K which will insert the value of the substitution after the char) with the ANSI escape sequence for "save cursor position" (\x1b[s), the inline list (@{[...]}) consisting of the next letter of the word ($F[1], conditionally surrounded by double backspaces if $| is truthy) and then the interpolated sub-list of the remaining letters in the word from index 2 (automatically joined with $" which we set earlier to backspace and "up one row", finally followed by the ANSI escape sequence for "restore cursor position" (\x1b[u).
$_ is then implicitly printed (via -p).
Perl 5 + -p040lF -MList::Util+max, 127 bytes
This solution also utilises ANSI escape sequences and necessitates a compatible terminal emulator, but it automatically moves the cursor to the correct position.
$\.=".[25B"." "x--$|.join($"="..[A",a..z)=~s/$F[0]\K/.[s@{[$|?"..$F[1]..":$F[1]]}@F[2..@F].[u/gr;$;=max$;,-125+@F+ord}{$_="."x$
Explanation
The majority of this is the same as the previous solution, except the output is stored in $\ instead of $_ (which is implicitly output after every call to print, which happens automatically at the end of the program due to -p).
For each input word (implicit -n from -p, stored in $_, split into chars in @F via -F) the following happens:
$\ has "\x1b[25B" added which is the ANSI escape sequence for "move the cursor down 25 lines" and, conditionally (depending on the alternating value of --$|), a space (can't use $" here as that could is overwritten in the first loop), concatenated with the result of the alphabet joining and substitution which is the same as the previous version.
Additionally, $; is set to the max of either $; (\x1c by default) or -125+@F+ord (where @F used in scalar context is the length of the list and ord is the ordinal of the first char of the input $_).
Outside of the implicit while loop from -n (-p), $_ is set to one space, followed by $; (using the implicit ; added from -n - while <STDIN> { ... ;}) repetitions of "\x0c" (form feed).
$_ is then output automatically via -p, which also outputs $\.
APL+WIN, 154 or 76 72 bytes
Original. Prompts for first string followed by second. Index origin = 0:
x←⎕⋄y←⎕⋄a←⎕av[17+⍳26]⋄m←((∊⌈/26⌈¨(n←¯2+¨⍴¨s)+i←a⍳↑¨s←x y),6)⍴' '⋄m[(↑i)+⍳↑n;0]←2↓x⋄m[↑i;1]←↑1↓x⋄m[;3]←m[;2]←(↑⍴m)↑a⋄m[∊1↓i;4]←↑1↓y⋄m[(∊1↓i)+⍳∊1↓n;5]←2↓y⋄⊖m
Try it online! Thanks to Dyalog Classic
New approach. Prompts for the two strings as a nested vector:
l←∊26+⌈/⍴¨s←⎕⋄⍉⌽3⊖a⍪((∊2⍴¨-a⍳↑¨⌽s)⌽1⊖⊃l↑¨(1⌷¨s),2↓¨⌽s)⍪a←l↑a←⎕av[17+⍳26]
JavaScript (Node.js), 151 bytes
Expects an array of two strings.
f=(a,y=i=[],c=e=Buffer([y>25?32:97+y]),b=a.map((w,k)=>w[0]==c?w[(i[k]=2)-k]+w[1+k]:(x=w[++i[k]])?e=k?" "+x:x+" ":" "))=>++e?c:f(a,-~y)+`
`+b.join(c+c)
Commented
f = ( // f is a recursive function taking:
a, // a[] = input
y = // y = vertical position
i = [], // i[] = array of positions in words
c = // c = current middle character
e = Buffer([ // e = copy of c
y > 25 ? 32 // use a space if y > 25
: 97 + y // otherwise, use 'a' ... 'z'
]), //
b = a.map((w, k) => // for each word w at index k in a[]:
w[0] == c ? // if the first letter of w is c:
w[ // append the 2nd or 3rd letter of w
(i[k] = 2) - k // and initialize i[k] to 2
] + //
w[1 + k] // append the other letter (3rd or 2nd)
: // else:
( // try to increment i[k]
x = w[++i[k]] // save w[i[k]] in x
) ? // if it's defined:
e = // set e to a non-zero'ish value
k ? " " + x // append a space followed by x
: x + " " // or x followed by a space
: // else:
" " // append 2 spaces
) // end of map()
) => ++e // if e is a space (i.e. we have y > 25
? // and both words have been fully written):
c // stop
: // else:
f(a, -~y) + // append the result of a recursive call
`\n` + // followed by a new line
b.join(c + c) // followed by b[] joined with c + c
J, 62 bytes
0|.@|:(,~|.)&((u:97+i.26)&([,](,"1(,@{.,:}.)@}.)~' '#~(i.{.)))
Probably more bytes to shave off here but not seeing them now...
Function that takes the left branch as the right arg and the right branch as the left arg.
Draws each half in canonical, horizontal form with branch underneath:
abcdefghijklmnopqrstuvwxyz
o
mebranch
Reverses the right half and prepends it.
Transposes and reverses.
Jelly, 25 23 bytes
ḢO_97⁶x;Ɱ,Ḣ$Øaṭ¹Ṛƭ)Ẏz⁶Ṛ
A monadic Link that accepts a pair of lists of characters and yields a list of lists of characters (lines).
Try it online! (The footer joins the lines with newline characters.)
How?
ḢO_97⁶x;Ɱ,Ḣ$Øaṭ¹Ṛƭ)Ẏz⁶Ṛ - Link: pair of lists of characters, Words
) - for each {Word in Words}:
Ḣ - pop the first character
O_97 - ordinal {of that} minus 97 -> height
⁶x - space character repeated {that many times}
$ - last two links as a monad - f(BeheadedWord)
Ḣ - pop the first character -> second character of Word
, - pair {the rest of BeheadedWord} with {that}
Ɱ - map across {these two WordParts} with:
; - concatenate {WordPart} to {RepeatedSpaces}
Øaṭ - tack on the lower-case alphabet
ƭ - alternate between:
¹ - do nothing (when processing the first Word)
Ṛ - reverse (when processing the second Word)
Ẏ - tighten
z⁶ - transpose with space character as filler
Ṛ - reverse
Vyxal, 28 bytes
vhøA‹ð*?vḢƛḣ$W;÷Ṙ"+n2εwYfvṘ§
I'm very rusty, this can probably be done in about 25 bytes.
Explanation:
vhøA‹ð*?vḢƛḣ$W;÷Ṙ"+n2εwYfvṘ§
vhøA‹ # For both words, push first item's position in alphabet
ð* # Multiply with " "
?vḢƛ ; # Map through suffix of each word:
ḣ$W # Extract head and rest, swap and wrap in list
÷Ṙ" # Reverse order of second item
+ # Append spaces to each pair
n2εw # Push alphabet twice, wrap in list
Yf # Interleave with formatted words and flatten
vṘ§ # Reverse each item, transpose and join by newlines
💎
Created with the help of Luminespire.
Charcoal, 25 bytes
↑E²βF²«‖SθJι±⌕β§θ⁰…θ²↑✂θ²
Try it online! Link is to verbose version of code. Explanation:
↑E²β
Output two copies of the alphabet vertically.
F²«
Loop over the two inputs.
‖
Reflect the canvas (this is golfier than trying to output the two words in different directions).
Sθ
Input the next word.
Jι±⌕β§θ⁰
Jump to its starting letter.
…θ²
Output the first two letters of the word.
↑✂θ²
Output the rest of the word upwards.
K (ngn/k), 53 bytes
Takes a list of 2 strings and returns a character matrix.
{|+(|/#'x)$x,:|y}.{(""[!97!*x],/:|1 2_x),,`c$97+!26}'
JavaScript (Node.js), 164 bytes
f=(a,b,p=![i=10],q=0,c=i>35?S:i.toString(++i),s=(p?(a[++p]||S)+S:a[0]==c?a[p=2]+a[1]:S+S)+c+c+(q?S+(b[++q]||S):b[0]==c?b[1]+b[q=2]:S))=>1/s?S:f(a,b,p,q)+`
`+s;S=' '