Blueprint a sestina

19

2

A sestina is a format of poem that follows an interesting pattern that we can generate. It has six stanzas of six lines each, where the last words of each line in the first stanza make up the line endings in each successive stanza, rotated in a set pattern. (There's also a three-line stanza at the end, but we won't worry about that.) Take a look at the first three stanzas of Elizabeth Bishop's creatively named Sestina:

September rain falls on the house.
In the failing light, the old grandmother
sits in the kitchen with the child
beside the Little Marvel Stove,
reading the jokes from the almanac,
laughing and talking to hide her tears.

She thinks that her equinoctial tears
and the rain that beats on the roof of the house
were both foretold by the almanac,
but only known to a grandmother.
The iron kettle sings on the stove.
She cuts some bread and says to the child,

It's time for tea now; but the child
is watching the teakettle's small hard tears
dance like mad on the hot black stove,
the way the rain must dance on the house.
Tidying up, the old grandmother
hangs up the clever almanac

...

Note how each line ends with one of the six words "house," "grandmother," "child," "stove," "almanac," or "tears." Not only that, but the words are ordered in the pattern 6–1—5–2—4–3, relative to the previous stanza. It ends up looking like a spiral:

enter image description here

We're still a few years away from programmatically generating a full sestina, but we can create a template featuring the end words of each stanza in proper order. Write a program or function that, given the six line ending words, outputs the blueprint for a sestina, following these rules. Here is the expected result for the input house grandmother child stove almanac tears:

house
grandmother
child
stove
almanac
tears

tears
house
almanac
grandmother
stove
child

child
tears
stove
house
grandmother
almanac

almanac
child
grandmother
tears
house
stove

stove
almanac
house
child
tears
grandmother

grandmother
stove
tears
almanac
child
house

The first stanza is the words in original order, the second stanza is in the order 6-1-5-2-4-3 from the first. The third stanza is that order relative to the second, and so on, all the way to stanza 6.

Assume that the input words will always be only letters, uppercase or lowercase. You can take them as an array of strings or a single string delimited by a non-letter character (space, newline, etc.). In the output, lines are separated by newlines (0x0A), and stanzas separated by two newlines. A trailing newline is acceptable.

This is , so shortest code in bytes wins. That being said, it may be shorter to compress the whole poem's structure, but I'd like to see some solutions that base each stanza on the previous.

NinjaBearMonkey

Posted 2016-11-12T17:23:53.403

Reputation: 9 925

Trailing newlines accepted? – Luis Mendo – 2016-11-12T18:36:23.130

Also, can the separator line contain a space? – Luis Mendo – 2016-11-12T18:42:40.893

@LuisMendo Sure, both are okay. – NinjaBearMonkey – 2016-11-12T18:43:58.750

Can the output be an ordered list of ordered lists of strings? – Greg Martin – 2016-11-12T19:13:22.437

@GregMartin I'm not sure I understand what you mean. It should be a string with no extraneous formatting, though. – NinjaBearMonkey – 2016-11-12T19:18:11.607

6+1 for sestinas, but I'm not sure this merits the natural-language tag. The algorithm is the same even if the input is six strings of gibberish. – DLosc – 2016-11-12T20:37:03.467

I read "siesta" and, after reading what is actually was, am now less confident in my ability to win. – Magic Octopus Urn – 2016-11-14T15:32:47.760

Answers

1

Jelly, 15 14 bytes

620œ?$ÐĿY€j⁷Ḥ¤

TryItOnline!

How?

Yey, a use of one of my additions to Jelly! (œ?)

620œ?$ÐĿY€j⁷Ḥ¤ - Main link: list of words L
      ÐĿ       - loop until no longer unique, collecting intermediate results
     $         -     last two links as a monad
   œ?          -         permutation of right argument (initially L) at index
620            -         620
        Y€     - join with line feeds for €each (the words of each stanza)
          j    - join (the stanzas) with
             ¤ - nilad followed by link(s) as a nilad
           ⁷   -     a line feed
            Ḥ  -     double (two line feeds)

Jonathan Allan

Posted 2016-11-12T17:23:53.403

Reputation: 67 804

7

Python, 72 64 bytes

i,n=input(),'\n';exec"print n.join(i)+n;i=map(i.pop,[-1,0]*3);"*6

Takes input through STDIN as a comma separated array of 6 strings and outputs to STDOUT in the format described in the post with an additional trailing newline.

Try It Online! (Ideone)

Also, I am not sure if this is okay to do, but here is a shorter answer in the form of an anonymous lambda function at 59 bytes that takes in input in the same format as the above answer, and outputs the program needed to generate the correct output:

lambda i,n='\n':"print n.join(i)+n;i=map(i.pop,[-1,0]*3);"*6

Therefore it must be called in the format exec(<Function Name>(<Array>)). Again, I am not sure if this is okay to do, so I am adding this as an extra, separate, non-competing answer until someone (maybe even OP) can hopefully clarify if this is okay or not, which I would really appreciate.

R. Kap

Posted 2016-11-12T17:23:53.403

Reputation: 4 730

2I like the pop trick! – xnor – 2016-11-12T23:19:36.687

3

MATL, 18 17 bytes

0ch5:"t[6l5H4I7])

Input is a cell array of strings, in the format

{'house' 'grandmother' 'child' 'stove' 'almanac' 'tears'}

Try it online!

Explanation

0c          % Push string with a single space, to be used as separator
h           % Input array of 6 strings implicitly and append the above string
5:"         % Repeat 5 times
  t         %   Duplicate the array of strings (previous stanza plus separator)
  [6l5H4I7] %   Push array [6 1 5 2 4 3 7]. The 7th string is the separator, and stays
            %   at the end. The other strings are shuffled as required
  )         %   Index into the array of strings
            % End implicitly
            % Display implicitly

Luis Mendo

Posted 2016-11-12T17:23:53.403

Reputation: 87 464

3

Mathematica, 59 bytes

r=Riffle;""<>Flatten@r[NestList[RotateRight,#,5],""]~r~"\n"&

The core of this unnamed function is NestList[RotateRight,#,5], which takes an input list of length 6 and creates a list of 6 lists, each rotated in the sestina way. Indeed, if a list of lists-of-strings is acceptable output, then NestList[RotateRight,#,5]& does the job in 26 bytes.

Then, r[...,""] inserts an empty string between each of the 6 lists; Flatten turns the whole thing into a single list of strings; ~r~"\n" then inserts a newline between each of those strings; and ""<> concatenates the whole thing into a single string. Thus the other 33 bytes are just to convert the structured output into a single string.

Greg Martin

Posted 2016-11-12T17:23:53.403

Reputation: 13 940

2

Batch, 99 bytes

@for %%w in (%*)do @if not .%%w==.%7 echo %%w
@echo(
@if not .%7==...... %0 %6 %1 %5 %2 %4 %3 .%7

Explanation: Takes input as command-line parameters. The %0 causes it to loop around, accumulating .s in the originally empty 7th parameter. The extra . is because if doesn't work on empty strings.

Neil

Posted 2016-11-12T17:23:53.403

Reputation: 95 035

2

Ruby, 51 bytes

->z{z.map{z[1],z[3],z[5],z[4],z[2],z[0]=z+[""]}*$/}

Instead of iterating over the numbers 0..5 as below, we repeat 6 times by iterating over the elements of z. In normal usage such as (0..5).map{|i|puts i} the code {} reads the elements iterated over. In this case the permutations done by the code inside the {} do not read the elements iterated over, so we can iterate over the elements of z without this interfering with the permutations.

Ruby, 56 bytes

Takes a 6 element array as a parameter

->z{(0..5).map{z[1],z[3],z[5],z[4],z[2],z[0]=z+[""]}*$/}

alternate version taking 6 parameters

->a,b,c,d,e,f{(0..5).map{b,d,f,e,c,a=a,b,c,d,e,f,""}*$/}

With each iteration of map we permute z. The original version plus a "" to represent a break between stanzas becomes the output of the map (this seventh array element is not needed by the assignment so is ignored). *$/ converts the arrays to a string, joining everything together with newlines.

Level River St

Posted 2016-11-12T17:23:53.403

Reputation: 22 049

2

Racket 115 bytes

(let p((o(list l))(m 0))(if(> n m)(p(cons(map(λ(x)(list-ref(list-ref o 0)x))'(5 0 4 1 3 2))o)(+ 1 m))(reverse o)))

Ungolfed:

(define(f l n)
 (let loop ((ol (list l))
             (m 0))
    (if (> n m) 
        (loop
         (cons (map
                (λ (x) (list-ref (list-ref ol 0) x))
                '(5 0 4 1 3 2))
               ol)
         (add1 m))
        (reverse ol))))

Testing:

(f (list "house" "grandmother" "child" "stove" "almanac" "tears") 6)

Output:

'(("house" "grandmother" "child" "stove" "almanac" "tears")
  ("tears" "house" "almanac" "grandmother" "stove" "child")
  ("child" "tears" "stove" "house" "grandmother" "almanac")
  ("almanac" "child" "grandmother" "tears" "house" "stove")
  ("stove" "almanac" "house" "child" "tears" "grandmother")
  ("grandmother" "stove" "tears" "almanac" "child" "house")
  ("house" "grandmother" "child" "stove" "almanac" "tears"))

rnso

Posted 2016-11-12T17:23:53.403

Reputation: 1 635