15
\$\begingroup\$

The Great Pyramid of Giza, the largest pyramid in Egypt, is not only the oldest of the Seven Wonders of the Ancient World, but it is also the only one to remain largely intact. The Egyptian Pyramids can take up to 20 years to build and are so big that Al-Aziz Uthman, son of the great Saladin who crushed the Crusaders, had to give up demolishing the Great pyramids of Giza because it was deemed too great a task. The Egyptian pyramids were mostly built as tombs for the country's Pharaohs and their consorts during the Old and Middle Kingdom periods (c. 2686–1690 BCE), and as of 2008, 138 Egyptian pyramids have been discovered.

The task is to create a program which inputs a sequence of distances separated by a space, and produces 10×10 text pyramids separated by those distances. A distance of 1 is equal to two characters.

A text pyramid will look like this:

         /\
        /--\
       /----\
      /------\
     /--------\
    /----------\
   /------------\
  /--------------\
 /----------------\
/------------------\

If the input consists of only a line break, then one pyramid will be produced, as above. For each pyramid, pyramids to the left are displayed as if they were in front.

Example I

Input:

4 3 1

Output:

         /\      /\    /\/\
        /--\    /--\  /--\-\
       /----\  /----\/----\-\
      /------\/------\-----\-\
     /--------\-------\-----\-\
    /----------\-------\-----\-\
   /------------\-------\-----\-\
  /--------------\-------\-----\-\
 /----------------\-------\-----\-\
/------------------\-------\-----\-\

Example II

Input:

0 9

Output:

         /\                /\
        /--\              /--\
       /----\            /----\
      /------\          /------\
     /--------\        /--------\
    /----------\      /----------\
   /------------\    /------------\
  /--------------\  /--------------\
 /----------------\/----------------\
/------------------\-----------------\

Example III

Input:

11

Output:

         /\                    /\
        /--\                  /--\
       /----\                /----\
      /------\              /------\
     /--------\            /--------\
    /----------\          /----------\
   /------------\        /------------\
  /--------------\      /--------------\
 /----------------\    /----------------\
/------------------\  /------------------\

The application to fulfill these requirements in the fewest amount of characters is the winner.

Reference: Wikipedia.org

\$\endgroup\$
4
  • \$\begingroup\$ I presume that additional whitespace at the end of the line is permitted? \$\endgroup\$ Jun 1, 2011 at 18:33
  • \$\begingroup\$ Depends who you ask. In the strictest reading of the spec, there is no whitespace following the output. But given that this is for fun, I have no problem with it. \$\endgroup\$
    – nharren
    Jun 1, 2011 at 22:32
  • \$\begingroup\$ So command line arguments for taking input is allowed? \$\endgroup\$
    – Joey
    Jun 3, 2011 at 14:07
  • \$\begingroup\$ As long as it meets the requirements. I do see now that Whitledge's solution is actually not able to handle linebreaks as input (I cannot undo my upvote), it simply works around it by producing a pyramid if there is no input. But if you can find a solution that can handle linebreak inputs (\r or \n is fine) as command line args, then it is okay with me. \$\endgroup\$
    – nharren
    Jun 4, 2011 at 2:38

10 Answers 10

5
\$\begingroup\$

Windows PowerShell, 122 132 133 139

$d=@(-split$input)-gt0
0..9|%{' '*(9-($x=$_))+($a="/$('--'*$_)\")+-join($d|%{'  '*(($_-$x-1)*($x-lt$_))
$a[(-2*$_)..-1]})}

Test script.

Random input also makes for nice images:

Random pyramids

\$\endgroup\$
3
  • \$\begingroup\$ It works if I add $input=Read-Host at the top, otherwise it doesn't ask for input. How should this be run? \$\endgroup\$
    – nharren
    Jun 1, 2011 at 2:07
  • \$\begingroup\$ @nharren: echo 0 3 4 1|powershell -noprofile -file pyramids.ps1 Or from PowerShell '0 1 4 3' | .\pyramids.ps1. This is a frequent problem with golfing in PowerShell, sadly, as you can only accept either piped-in input or interactive input. PowerShell doesn't really have the notion of stdin other languages and environments have and this shows sometimes. I usually go for piped input, unless the task explicitly calls for interactivity, like Guess the number. \$\endgroup\$
    – Joey
    Jun 1, 2011 at 9:45
  • \$\begingroup\$ Ah yes, now it works. My button mashing wasn't yielding any results, and I couldn't figure out why. \$\endgroup\$
    – nharren
    Jun 1, 2011 at 11:39
4
\$\begingroup\$

Golfscript, 70 characters

~]0-:|;10,{:§9\-" "*"/""-"§2**+"\\"+:&|{.§>{§-(2*" "*1$}{-2*&>}if}%n}%

Direct port of my Ruby solution, so I'm sure it's possible to shorten this by quite a few characters.

\$\endgroup\$
4
\$\begingroup\$

Haskell, 148 characters

r=replicate
p d=map(\k->foldr(\n i->r(9-k)' '++'/':r(2*k)'-'++"\\"++drop(11+k)(r(2*n)' '++i))""$d++[0])[0..9]
main=interact$unlines.p.map read.words

I'm quite unsatisfied with this! It just feels way too long. Ideas?

\$\endgroup\$
2
  • \$\begingroup\$ Inside the lambda, you could change the big pile of ++'s to a single list and use concat aka >>=id. I don't know whether it will help. Another point would be using foldr1 instead of foldr. \$\endgroup\$
    – FUZxxl
    Jun 1, 2011 at 13:51
  • \$\begingroup\$ Thanks for the ideas. Neither helps in this case: Converting ++ sequences only saves you one character per item, and the overhead of the final concat is too high here. The foldr can't use the foldr1 form as the result time is String whereas the list type is [Int] (The 1 variants fold require them to be the same.) \$\endgroup\$ Jun 5, 2011 at 0:04
4
\$\begingroup\$

Python, 123 characters

N=[10]+map(int,raw_input().split())
for y in range(10):print''.join((2*n*' '+'/'+2*y*'-'+'\ ')[-2*n-1:-1]for n in N)[9-y:]
\$\endgroup\$
2
  • \$\begingroup\$ Out of curiosity, is this python 2.5? To get this to work in python 3.2, I wrapped the map function in a list function, changed raw_input() to input(), and changed print to print(). \$\endgroup\$
    – nharren
    Jun 2, 2011 at 6:33
  • \$\begingroup\$ @nharren: works in both 2.4.4 and 2.5.2 for me. \$\endgroup\$ Jun 2, 2011 at 6:44
4
\$\begingroup\$

Ruby 1.9, 116 characters

d=gets.split-[?0]
10.times{|i|puts [?\s*(9-i),l=?/+?-*2*i+?\\,d.map{|r|i<(r=r.to_i)??\s*2*(r+~i)+l :l[-2*r,99]}]*""}
\$\endgroup\$
2
\$\begingroup\$

Perl, 130 126 132 chars

$_=<>;$s=" "x9;map{$k.="/\\"."  "x($_-1)if$_}split;$_="$s$k/\\$s\n";for$i(0..9){print;s%\\-%-\\%g;s%\\/%-\\%g;s%\\ %-\\%g;s% /%/-%g}

Slightly shorter version which takes input as command-line arguments rather than from stdin:

$s=" "x9;map{$k.="/\\"."  "x($_-1)if$_}@ARGV;$_="$s$k/\\$s\n";for$i(0..9){print;s%\\-%-\\%g;s%\\/%-\\%g;s%\\ %-\\%g;s% /%/-%g}

Can't believe no-one did a regex solution yet. Perl is a long way from being my best language, so this can probably lose a lot more. I'd be interested to see a sed implementation, if someone's up for the challenge.

(Thanks, @mbx, for 4 chars).

\$\endgroup\$
7
  • \$\begingroup\$ foreach==for -> save 4 chars \$\endgroup\$
    – mbx
    Jun 2, 2011 at 2:36
  • \$\begingroup\$ have you tested your version with the given testcases?! \$\endgroup\$
    – mbx
    Jun 2, 2011 at 2:45
  • \$\begingroup\$ @mbx, yes, works for me. Perl 5.10.1, Ubuntu. What bug are you seeing? \$\endgroup\$ Jun 2, 2011 at 5:42
  • \$\begingroup\$ @Peter Taylor - on my ubuntu and win32 it works also fine. I first tried it on ideone which is running perl 5.12.1. \$\endgroup\$
    – mbx
    Jun 2, 2011 at 9:08
  • 2
    \$\begingroup\$ »If the input consists of only a line break« hints at standard input, actually. \$\endgroup\$
    – Joey
    Jun 2, 2011 at 11:13
1
\$\begingroup\$

JavaScript, 396 bytes

function p(a){for(u=0;u<10;u++){t[u+a][9-u]="/";for(k=9-u+1+a;k<10+u+a;k++)t[k][u]="-";
t[10+u+a][u]="\\"}}function _(a){t=[];for(i=0;i<50;i++){t[i]=[];for(j=0;j<10;j++)t[i][j]=" "
}var a=a.split(" "),b=a.reduce(function(a,b){return a-0+(b-0)})*2;for(i=a.length-1;i>=0;
i--)p(b),b-=a[i]*2-0;p(0);a="";for(j=0;j<10;j++){b="";for(i=0;i<50;i++)b+=t[i][j];
a+=b.replace(/\s+$/,"")+(j<9?"\n":"")}return a}

I'm not going to win with JavaScript, but there is a JavaScript entry now :)

Usage: _("1 2 3") etc.

\$\endgroup\$
1
\$\begingroup\$

Ruby (112)

Slightly shorter than Ventero's Ruby solution, with a different approach. I just started learning Ruby, so this can probably be reduced quite a bit.

s=' '*9+r='/\\';gets.split.map{|i|s+=' '*2*(i.to_i-1)+r}
10.times{puts s;s.gsub!' /','/-';s.gsub!(/\\.?/,'-\\')}
\$\endgroup\$
1
\$\begingroup\$

Powershell, 105 98 bytes, the strictest reading of the spec

-7 bytes from migimaru's answer.

($a=' '+-join(,5+$args-gt0|%{'  '*--$_+'/\'}))
1..9|%{($a=$a-replace' /','/-'-replace'\\.?','-\')}

Test script:

$f = {

($a=' '+-join(,5+$args-gt0|%{'  '*--$_+'/\'}))
1..9|%{($a=$a-replace' /','/-'-replace'\\.?','-\')}

}

@(
,(@"
         /\
        /--\
       /----\
      /------\
     /--------\
    /----------\
   /------------\
  /--------------\
 /----------------\
/------------------\
"@)

,(@"
         /\      /\    /\/\
        /--\    /--\  /--\-\
       /----\  /----\/----\-\
      /------\/------\-----\-\
     /--------\-------\-----\-\
    /----------\-------\-----\-\
   /------------\-------\-----\-\
  /--------------\-------\-----\-\
 /----------------\-------\-----\-\
/------------------\-------\-----\-\
"@, 4,3,1)

,(@"
         /\                /\
        /--\              /--\
       /----\            /----\
      /------\          /------\
     /--------\        /--------\
    /----------\      /----------\
   /------------\    /------------\
  /--------------\  /--------------\
 /----------------\/----------------\
/------------------\-----------------\
"@, 0,9)

,(@"
         /\                    /\
        /--\                  /--\
       /----\                /----\
      /------\              /------\
     /--------\            /--------\
    /----------\          /----------\
   /------------\        /------------\
  /--------------\      /--------------\
 /----------------\    /----------------\
/------------------\  /------------------\
"@, 11)
) | % {
    $expected, $a = $_
    $result = &$f @a
    ($result-join"`n")-eq$expected
    $result 
}

Output:

True
         /\
        /--\
       /----\
      /------\
     /--------\
    /----------\
   /------------\
  /--------------\
 /----------------\
/------------------\
True
         /\      /\    /\/\
        /--\    /--\  /--\-\
       /----\  /----\/----\-\
      /------\/------\-----\-\
     /--------\-------\-----\-\
    /----------\-------\-----\-\
   /------------\-------\-----\-\
  /--------------\-------\-----\-\
 /----------------\-------\-----\-\
/------------------\-------\-----\-\
True
         /\                /\
        /--\              /--\
       /----\            /----\
      /------\          /------\
     /--------\        /--------\
    /----------\      /----------\
   /------------\    /------------\
  /--------------\  /--------------\
 /----------------\/----------------\
/------------------\-----------------\
True
         /\                    /\
        /--\                  /--\
       /----\                /----\
      /------\              /------\
     /--------\            /--------\
    /----------\          /----------\
   /------------\        /------------\
  /--------------\      /--------------\
 /----------------\    /----------------\
/------------------\  /------------------\

Powershell, 101 94, fun with one leading whitespace

($a=-join(,6+$args-gt0|%{'  '*--$_+'/\'}))
1..9|%{($a=$a-replace' /','/-'-replace'\\.?','-\')}
\$\endgroup\$
0
\$\begingroup\$

I couldn't get the C#3 version any shorter than this. I don't know the character count exactly, but I suspect that I have lost. :-(

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace PyramidRenderer
{
    /// <summary>
    /// Generates ASCII-art pyramids at user-specified horizontal locations to
    /// the standard output stream.
    /// </summary>
    public class Program
    {
        /// <summary>
        /// Generates one or more ASCII-art pyramids at the locations specified and 
        /// sends them to the standard output stream.
        /// </summary>
        /// <param name="args">The command-line arguments. These should be non-negative 
        /// integers that specify the horizontal distance of each additional pyramid from the 
        /// preceeding pyramid. Whether or not any distances are suppplied, a pyramid
        /// is rendered at the starting location.</param>
        public static void Main(string[] args)
        {
            try
            {
                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

                int[] pyramidDistances = ParsePyramidLocationsFromCommandLine(args).ToArray();
                PyramidCollection pyramids = new PyramidCollection(pyramidDistances);
                pyramids.RenderToText(Console.Out);
            }
            catch (ArgumentException ex)
            {
                Console.Error.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Handler for the unhandled exception. This just displays the error message to 
        /// the standard error stream.
        /// </summary>
        /// <param name="sender">Required but unnecessary sender object for the event handler.</param>
        /// <param name="e">The object that represents the exception.</param>
        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Debug.Assert(e.ExceptionObject != null);

            string exceptionText;
            Exception ex = e.ExceptionObject as Exception;
            if (ex == null)
                exceptionText = e.ExceptionObject.ToString();
            else
                exceptionText = ex.Message;
            Console.Error.WriteLine(exceptionText);
        }

        /// <summary>
        /// Takes the command-line arguments and converts them to a sequence of 
        /// non-negative integers.
        /// </summary>
        /// <param name="args">The command-line arguments as supplied to Main.</param>
        /// <returns>A sequence of integers that represent the user’s distance selections.</returns>
        /// <exception cref="ArgumentException">An invalid argument was supplied.</exception>
        private static IEnumerable<int> ParsePyramidLocationsFromCommandLine(string[] args)
        {
            Debug.Assert(args != null);

            foreach (string arg in args)
            {
                int result;
                if (int.TryParse(arg, out result))
                {
                    if (result < 0)
                        throw new ArgumentException(string.Format("Invalid distance specified: {0}", arg));

                    yield return result;
                }
                else
                {
                    throw new ArgumentException(string.Format("Invalid option: {0}", arg));
                }
            }
        }
    }

    /// <summary>
    /// Represents a single pyramid to be rendered.
    /// </summary>
    internal class Pyramid
    {
        /// <summary>
        /// The height of the pyramids in text rows. The width of each pyramid will be
        /// twice the height.
        /// </summary>
        internal const int Height = 10;

        /// <summary>
        /// The length in characters of the horizontal unit distance in which the user 
        /// specifies the pyramid distances.
        /// </summary>
        internal const int PyramidUnits = 2;

        /// <summary>
        /// The character to output as the background of the pyramids.
        /// </summary>
        private const char backgroundChar = ' ';

        /// <summary>
        /// The character to output as the left edge of the pyramids.
        /// </summary>
        private const char leftEdgeChar = '/';

        /// <summary>
        /// The character to output within each pyramid, between the edges.
        /// </summary>
        private const char brickChar = '-';

        /// <summary>
        /// The character to output as the right edge of the pyramids.
        /// </summary>
        private const char rightEdgeChar = '\\';

        /// <summary>
        /// The absolute horizonal location of the pyramid’s bottom left corner as 
        /// specified in PyramidUnits.
        /// </summary>
        private int position;

        /// <summary>
        /// Constructs a new pyramid object at the specified location.
        /// </summary>
        /// <param name="position">The absolute horizonal location of the pyramid’s bottom
        /// left corner in PyramidUnits.</param>
        internal Pyramid(int position)
        {
            Debug.Assert(position >= 0);

            this.position = position;
        }

        /// <summary>
        /// Renders a single row the pyramid to the supplied text stream starting at
        /// the indicated location.
        /// </summary>
        /// <param name="textWriter">The output stream to which the pyramid is to
        /// be rendered.</param>
        /// <param name="row">The row of the pyramid to render. Zero is the top row,
        /// and Height - 1 is the bottom row.</param>
        /// <param name="startingPosition">The text character position—indexed at zero—at 
        /// which the rendering is to begin. If non-zero, this identifies the column one 
        /// past the ending location of the previous pyramid.</param>
        /// <returns>The horizontal location (in characters) at which the next item 
        /// may be rendered.</returns>
        internal int RenderRow(TextWriter textWriter, int row, int startingPosition)
        {
            Debug.Assert(textWriter != null);
            Debug.Assert(row >= 0);
            Debug.Assert(startingPosition >= 0);

            int leftBoundary = Height - 1 - row + position * PyramidUnits;
            int rightBoundary = Height + row + position * PyramidUnits;

            startingPosition = RenderField(textWriter, backgroundChar, startingPosition, leftBoundary);
            startingPosition = RenderField(textWriter, leftEdgeChar, startingPosition, leftBoundary + 1);
            startingPosition = RenderField(textWriter, brickChar, startingPosition, rightBoundary);
            startingPosition = RenderField(textWriter, rightEdgeChar, startingPosition, rightBoundary + 1);
            return startingPosition;
        }

        /// <summary>
        /// Outputs a sequence of repeated characters from the indicated starting position to
        /// just before the ending position, unless the starting position is already equal to
        /// or greater than the ending position.
        /// </summary>
        /// <param name="textWriter">The output stream to which the field is to be rendered.</param>
        /// <param name="character">The character to be repeated in the output.</param>
        /// <param name="startingPosition">The location at which rendering may begin in 
        /// characters indexed at zero.</param>
        /// <param name="endingPosition">The location one past the location at which rendering
        /// is to end.</param>
        /// <returns>The position at which the next field may begin.</returns>
        private static int RenderField(TextWriter textWriter, char character, int startingPosition, int endingPosition)
        {
            Debug.Assert(textWriter != null);
            Debug.Assert(startingPosition >= 0);
            Debug.Assert(endingPosition >= 0);

            int charCount = endingPosition - startingPosition;
            if (charCount <= 0)
                return startingPosition;
            textWriter.Write(new string(character, charCount));
            return endingPosition;
        }
    }

    /// <summary>
    /// A collection of pyramids to be displayed.
    /// </summary>
    internal class PyramidCollection
    {
        /// <summary>
        /// A left-to-right ordered list of the pyramids that the user has 
        /// requested to be rendered.
        /// </summary>
        List<Pyramid> allPyramids = new List<Pyramid>();

        /// <summary>
        /// Constructs a new pyramid collection.
        /// </summary>
        /// <param name="distances">The distances of each non-leftmost pyramid (in PyramidUnits) after
        /// the previous pyramid. The total number of pyramids will be one greater than the length of
        /// the distances array.</param>
        internal PyramidCollection(int[] distances)
        {
            Debug.Assert(distances != null);

            int nextPosition = 0;
            allPyramids.Add(new Pyramid(nextPosition));
            foreach (int nextDistance in distances)
            {
                Debug.Assert(nextDistance >= 0);

                try
                {
                    checked
                    {
                        nextPosition += nextDistance;
                        int endLocation = nextPosition * Pyramid.PyramidUnits + Pyramid.Height * 2;
                    }
                }
                catch (OverflowException)
                {
                    // Ignore any pyramids specified beyond the integer maximum distance.
                    break;
                }
                allPyramids.Add(new Pyramid(nextPosition));
            }
        }

        /// <summary>
        /// Outputs ASCII-art images of the pyramids in this collection to the 
        /// provided output stream.
        /// </summary>
        /// <param name="textWriter">The output stream to which the pyramids
        /// are to be rendered.</param>
        internal void RenderToText(TextWriter textWriter)
        {
            Debug.Assert(textWriter != null);

            for (int row = 0; row < Pyramid.Height; row++)
            {
                int startingPosition = 0;
                foreach (Pyramid pyramid in allPyramids)
                {
                    startingPosition = pyramid.RenderRow(textWriter, row, startingPosition);
                }
                textWriter.WriteLine();
            }
        }
    }

}
\$\endgroup\$
4
  • 3
    \$\begingroup\$ Did you confuse Code Bowling and Code Golf by any chance? \$\endgroup\$
    – Joey
    Jun 2, 2011 at 17:53
  • 1
    \$\begingroup\$ At least pretend to try. People won't hold a verbose language against you if you golf it. \$\endgroup\$ Jun 3, 2011 at 0:51
  • \$\begingroup\$ I guess that is your verbose version to explain your nifty tricks. It seems you forgot to post the golf version of it. \$\endgroup\$
    – mbx
    Jun 5, 2011 at 14:59
  • \$\begingroup\$ @Joey, @dmckee - I thought about doing a golf version, but haven't got around to it. I am terrible at this game anyway. Obscure code goes completely against my nature. I should probably stay away from the golf puzzles! - @mbx - Sadly, there are no nifty tricks. \$\endgroup\$ Jun 6, 2011 at 14:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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