Programming Challenge!

Twenty years ago, during my first year in academia, my Pascal tutor set us some top-notch assignments.

Your mission:

Write a program which draws a diamond of the form illustrated below. The letter which is to appear at the widest point of the figure (E in the example) is to be specified as input data.

Here’s a scan of the original hand-out:

My original Pascal solution, which I prototyped using BBC Basic V, took less than a page of fan-fold listing paper and was implemented as a console application. 50% of the listing dealt with input validation and “do you want to run the program again?” code! I will convert the example line-by-line into C# for later publication here! Of course, were I to write it today, it should look very different!

I’d be keen to see your solutions, written in your choice of programming language. Novelty value for uniqueness in your choice of programming language may well be rewarded! Procedural, object-oriented, functional, dynamic, verbose, terse…the choice is yours!

“What’s in it for me?” you might ask?
Well, nothing really, a bit of kudos and the feeling of a job well done! However, I will offer the two best/novel UK-based solutions a much-coveted DDD polo shirt (modelled here!). My decision is final, colour may vary, size might not be the same size as you, yada yada, other legalese applies, etc.

Submission by comments here on this post, by e-mail (top right About Me), or via Twitter @camurphy please!

Over to you!

UPDATE: Comments seem to mangle the code formatting, it has been suggested that code is submitted either via e-mail or via http://pastebin.com/

120 thoughts on “Programming Challenge!”

  1. This is what I came up with… sure there must be a more elegant way!

    static void Main(string[] args)
    {
    char c = Console.ReadKey().KeyChar;
    Console.WriteLine();
    const int numberOfRows = 5;
    int rowNumber = 0;
    List rows = new List();
    int numberOfColumns = (numberOfRows * 2) – 1;
    while (rowNumber Console.WriteLine(x));
    rows.Reverse();
    rows.RemoveAt(0); //we only want one “middle” row
    rows.ForEach(x => Console.WriteLine(x));
    }

  2. @mike – good solution; however I should perhaps have made it clear in the “spec” that the diamond must start at ‘A’ and roll on to the letter entered, i.e. it’s not limited to 5 rows!

  3. Ahh I see! Well replacing the line
    const int numberOfRows = 5;
    with
    int numberOfRows = (int)c – (int)’A’ + 1;
    should sort that…

  4. Something like this?

    class Program
    {
    private const string alphabet = “abcdefghijklmnopqrstuvwxyz”;

    static void Main(string[] args)
    {
    char c = Console.ReadKey().KeyChar;

    var myChars = from character in alphabet
    where character <= c
    select character;

    var fullRow = myChars.Skip(1).Reverse().Concat(myChars);

    var myRows = (from character in myChars
    select ConvertToSpaces(character, fullRow));

    IEnumerable reverseRows = myRows.Reverse().Skip(1);
    myRows = myRows.Concat(reverseRows);

    Console.WriteLine(Environment.NewLine);//Skip the ReadKey line
    foreach (string line in myRows)
    Console.WriteLine(line);

    Console.ReadKey();
    }

    private static string ConvertToSpaces(char wanted, IEnumerable characters)
    {
    return new string(
    (from character in characters
    select character == wanted ? character : ‘ ‘).ToArray());
    }

    }

  5. Here is your T-SQL version, not a proper DB optimised version, most definitely a programmer wrangling T-SQL to his will. Works though.

    —– —– —– —– —– —– —– —– —–

    — Test on SQL Server 2000 using SQL 2005 Management Studio

    DECLARE @OutmostChar AS CHAR(1)
    SET @OutmostChar = ‘Z’

    DECLARE @Result VARCHAR(2100)
    SET @Result = ”

    DECLARE @OutmostCharAscii INT
    SET @OutmostCharAscii = ASCII(@OutmostChar)

    IF (@OutmostCharAscii ASCII(‘Z’))
    BEGIN
    RAISERROR (N’Outmost character must be in range A-Z’, 10, 1);
    END

    DECLARE @NumberOfRowsToMiddle INT
    SET @NumberOfRowsToMiddle = @OutmostCharAscii – ASCII(‘A’);

    DECLARE @LoopVar INT
    SET @LoopVar = 0

    WHILE @LoopVar 0)
    BEGIN
    SET @Result = @Result + SPACE((@LoopVar * 2) – 1) +
    CHAR(ASCII(‘A’) + @LoopVar)
    END

    SET @Result = @Result + CHAR(13) + CHAR(10)

    SET @LoopVar = @LoopVar + 1
    END

    SET @LoopVar = @NumberOfRowsToMiddle
    WHILE @LoopVar >= 0
    BEGIN
    SET @Result = @Result + SPACE(@NumberOfRowsToMiddle – @LoopVar) +
    CHAR(ASCII(‘A’) + @LoopVar)

    IF (@LoopVar > 0)
    BEGIN
    SET @Result = @Result + SPACE((@LoopVar * 2) – 1) +
    CHAR(ASCII(‘A’) + @LoopVar)
    END

    SET @Result = @Result + CHAR(13) + CHAR(10)

    SET @LoopVar = @LoopVar – 1
    END

    SELECT @Result

    — In Results pane, right click single result and select “Save Results As ..” to save as CSV file to view in Notepad

  6. Since you requested it via twitter, here’s a LINQ sample. I cheated on the top and bottom lines with the ALine variable. Also, the For Each enumerations would be simplified in VB 10 using Statement Lambdas which are not available in VB 9 yet. I didn’t include validation on the input (but that would be trivial with a regular expression).

    Sub Main()
    Console.WriteLine(“Input the hightest letter to display. Leave blank to exit.”)
    Dim HighestLetter = Console.ReadKey
    If HighestLetter.Key = ConsoleKey.Enter Then Exit Sub
    Dim ascLetter = Asc(HighestLetter.KeyChar)
    Console.WriteLine()

    Dim ALine = New String(” “c, ascLetter – 65) & “A”

    Dim lines = (From c In Enumerable.Range(66, ascLetter – 65) _
    Select line = New String(” “c, ascLetter – c) & Chr(c) & New String(” “c, (c – 66) * 2 + 1) & Chr(c), _
    currentChar = c).ToArray

    Console.WriteLine(ALine)
    For Each line In lines
    Console.WriteLine(line.line)
    Next
    For Each line In lines.OrderByDescending(Function(l) l.currentChar).Skip(1)
    Console.WriteLine(line.line)
    Next
    Console.WriteLine(ALine)

    Main() ‘ Loop for more tries
    End Sub

  7. I have the permission of one of the lads in work to post this.
    Tom Vanhoutte he did it in about 20 minutes.

    Dim res()
    IDList=”ABCDEFGHIJKLMNOP”
    Index1= Instr(IDList,”F”)

    For i=1 to (Index1*2)-1
    ReDim res(60)
    res2=””
    pleft = Abs(i-Index1)
    Spaces=(Index1-(pleft+1))
    pChar= mid(IDList,abs(Spaces)+1,1)
    res(pleft)=pchar
    res(pleft+(spaces*2))=pchar
    for j=0 to 60
    if IsEmpty(res(j)) then res(j)=” ”
    res2=res2 & res(j)
    next
    rs=rs& res2 & vbcrlf
    next
    msgbox rs

  8. Here’s mine – I cheated slightly because I left in the double AA at the top and the bottom but I prefer it that way, it feels more balanced! 🙂

    ConsoleKeyInfo key = Console.ReadKey();
    Console.Clear();

    var query =
    from i in Enumerable.Range(0, ((key.KeyChar – ‘A’) * 2) + 1)
    let characterRange = key.KeyChar – ‘A’
    let distanceFromMiddle = Math.Abs(characterRange – i)
    let distanceFromEitherEnd = Math.Abs(characterRange – distanceFromMiddle)
    let character = (char)(‘A’ + distanceFromEitherEnd)
    select new string(‘ ‘, distanceFromMiddle) + character + new string(‘ ‘, distanceFromEitherEnd * 2) + character;

    foreach (var item in query)
    {
    Console.WriteLine(item);
    }

  9. C’mon Mr Taulty there is no excuses for cheating – even slightly… This “bug” fix might help you!

    ConsoleKeyInfo key = Console.ReadKey();
    Console.Clear();

    var query =
    from i in Enumerable.Range(0, ((key.KeyChar – ‘A’) * 2) + 1)
    let characterRange = key.KeyChar – ‘A’
    let distanceFromMiddle = Math.Abs(characterRange – i)
    let distanceFromEitherEnd = Math.Abs(characterRange – distanceFromMiddle)
    let character = (char)(‘A’ + distanceFromEitherEnd)
    select new string(‘ ‘, distanceFromMiddle) + character + (distanceFromEitherEnd == 0 ? String.Empty : new string(‘ ‘, (distanceFromEitherEnd * 2) – 1) + character);

    foreach (var item in query)
    {
    Console.WriteLine(item);
    }

  10. This is F#. Well, one way of doing it in F# 🙂 No, I didn’t choose to do it that way because it’s the easiest to understand.

    #light

    open System

    let endchar = Char.ToUpper (Console.ReadKey(true).KeyChar)

    let rec lines c bs ms = seq {
    let ss n = new String(‘ ‘, n)
    let ts c = string(char(c))
    let s = (ss bs) + (ts c) + (if ms = 0 then “” else (ss ms) + (ts c)) + (ss bs)
    yield s
    if bs > 0 then
    yield! (lines (c + 1) (bs – 1)
    (if ms = 0 then 1 else ms + 2))
    yield s
    }

    Seq.iter (fun (l:string) -> Console.WriteLine(l))
    (lines (int(‘A’)) (int(endchar) – int(‘A’)) 0)

  11. Hm… in F#, indentation does in fact matter. Let me see if the comments can contain HTML here, maybe that will correct the formatting:

    #light
     
    open System
     
    let endchar = Char.ToUpper (Console.ReadKey(true).KeyChar)
     
    let rec lines c bs ms = seq {
        let ss n = new String(‘ ‘, n)
        let ts c = string(char(c))
        let s = (ss bs) + (ts c) + (if ms = 0 then "" else (ss ms) + (ts c)) + (ss bs)
        yield s
        if bs > 0 then
            yield! (lines (c + 1) (bs – 1)
                     (if ms = 0 then 1 else ms + 2))
            yield s
        }
     
    Seq.iter (fun (l:string) -> Console.WriteLine(l))
      (lines (int(‘A’)) (int(endchar) – int(‘A’)) 0)
     

  12. Here’s a solution in Smalltalk…

    [code]
    | aChar startNum stopNum chars ctr totalChars |

    “GS – Assume chars will be ASCII uppercase only”
    startNum := 65.

    “GS – Set the Character to print up to”
    aChar := $E.

    “GS – Set the stop number”
    stopNum := aChar asUppercase asInteger.

    “GS – Fill a collection with arrays representing the lines in half of the diamond”
    totalChars := stopNum – startNum.
    ctr := 0.
    chars := OrderedCollection new.
    startNum to: stopNum do: [ : i |
    chars addLast:
    (Array
    “GS – Spaces from the left edge”
    with: (totalChars – ctr)
    “GS – The char to print”
    with: (i asCharacter asUppercase)
    “GS – The space between the two printed chars”
    with: ((ctr * 2) -1)).
    ctr := ctr + 1.
    ].

    “GS – Clear the Transcript window”
    Transcript clear.

    “GS – Print the top half of the diamond”
    chars do: [ : a |
    (a at: 1)
    timesRepeat:[Transcript show: Character space ].
    Transcript show: (a at: 2).
    ((a at: 3) == -1)
    ifTrue:[Transcript cr.]
    ifFalse:[
    (a at: 3)
    timesRepeat:[Transcript show: Character space. ].
    Transcript show: (a at: 2); cr.
    ].
    ].

    “GS – Print the bottom half of the diamond”
    ctr := 0.
    chars reverseDo: [ : a |
    (ctr == 0)
    ifFalse:[
    (a at: 1)
    timesRepeat:[Transcript show: Character space ].
    Transcript show: (a at: 2).
    ((a at: 3) == -1)
    ifTrue:[Transcript cr.]
    ifFalse:[
    (a at: 3)
    timesRepeat:[Transcript show: Character space. ].
    Transcript show: (a at: 2); cr.
    ].
    ].
    ctr := ctr + 1.
    ].

    [/code]

  13. Solution in PERL: (not the most elegant way of doing it I’m sure, but it works!)
    http://pastebin.com/f459f32ce

    print “Input your character: ” . “\r\n”;
    $c = getc(STDIN);
    $NumberOfRows = ord($c) – ord(“A”);
    $rowNumber = 0;
    $NumberOfColumns = ($NumberOfRows*2) -1;

    $rowNumber = 0;
    $bool=0;
    $multiple = 1;

    while ($rowNumber <= $NumberOfRows*2)
    {
    if ($rowNumber == $NumberOfRows+1)
    {
    $bool = 1;
    $multiple = -1;
    }

    $char = chr(ord($c) + ((-$NumberOfRows + $rowNumber) * $multiple));
    print ” ” x (($NumberOfRows – $rowNumber)*$multiple) . $char;

    if ($char gt “A”){print ” ” x ((($NumberOfRows*4*$bool) + ($rowNumber*2*$multiple))-1). $char;}
    print “\r\n”;
    $rowNumber++;
    }

  14. Bloody hell, that solution sucks, look at the size of it!!! Hmm, the Smalltalk is really rusty. I’ll be back with a better solution later 😉

  15. Eep even some duplicate code in my example ($rowNumber is THAT important that I reset it twice…)

    I tried a version in LOLCODE but the lack of multiplication and conversion from char to int means some nasty code would be required and I don’t have the time 🙂

  16. Just a thought but doesn’t Craig used the “Google Syntax Highlighter for WordPress” plugin, which should also work for comments? Although as it probably doesn’t support most of these languages, maybe simply wrapping code in pre tags would be a simpler solution?

  17. Here it is in PowerShell…

    Param([char]$letter = “E”, [int]$padding=5)
    $start = [int][char]”B”
    $end = [int]$letter

    $outerpadding = ($end – $start) + $padding
    $innerpadding = -1

    $lines = }

    $lines
    $lines[$($lines.Length-2)..0]

  18. Ooh, nice, it ate my whole scriptblock looking for an ; Guess doesn’t work. Let’s try that again, shall we? If it’s still horrible, look here http://poshcode.org/1029

    Param([char]$letter = "E", [int]$padding=5)
    $start = [int][char]"B"
    $end = [int]$letter

    $outerpadding = ($end - $start) + $padding
    $innerpadding = -1

    # this line ends with an ampersand followed by a {
    $lines = &{
    "$(" " * $outerpadding)A"
    foreach($char in ([string[]][char[]]($start..$end))) {
    $innerpadding += 2; $outerpadding--
    "$(" " * $outerpadding)$char$(" " * $innerpadding)$char"
    }
    }

    $lines
    $lines[$($lines.Length-2)..0]

  19. How about a single Excel formula? (tested in Excel 2007 and OpenOffice Calc 3.0)

    To create this solution,

    1) Create a new workbook in Excel.
    2) Format Columns A to AZ to 2.5 wide.
    3) Cell A1 should contain letter A-Z.
    4) Copy following formula to all cells in range Row 2 to Row 52, columns B to AZ,

    =IF(ROW() – CODE($A$1) + CODE(“A”) – 2 <=0, IF(ABS(COLUMN()-27)+2 = ROW(), CHAR(CODE(“A”) + ABS(COLUMN() – 27)), “”), IF((((CODE($A$1) – CODE(“A”)) * 2) + 2) – ROW() = ABS(COLUMN() – 27), CHAR(CODE(“A”) + ABS(COLUMN() – 27)), “”))

    Or just download the spreadsheet as a ZIP from here;

    http://www.tigernews.co.uk/blog-twickers/Excel-Diamond.zip

  20. Here’s another perl one, hehe… pass in the final letter, lower-case, on the command line.

    #!/usr/bin/perl

    $e=ord($ARGV[0]);
    $d=$e-ord(“a”);
    $_=” ” x $d . “a” . ” ” x $d;$\=”\n”;
    sub i { return chr(ord($_[0])+1); }

    while($e) {
    print;push(@l,$_);
    s/( )([a-z])( +)(\2)( )/i($2) . $3 . ” ” . i($2)/e;s/ a /b b/;$e=/^ /;
    }
    print;
    while($_=pop(@l)){print;}

  21. So, here’s hoping the formatting is going to work… if not, please find the well formatted piece of code that this should be at this URL: http://www.sturmnet.org/download/diamond.pl.html

    This is the same Perl program as before, but I thought I’d do the occasion justice and make it look nice, too. Yes, it still works!

    #!/usr/bin/perl

    $e=
    ord ($ARGV
    [0]); $d=$e-
    ord( “a”);
    $_=” ” x $d
    . “a” . ” ”
    x $d; $\=”\n”
    ;sub i {return
    chr( ord(
    $_[0] )+1);
    }while ($e){
    print ;push
    (@l, $_);
    s/( )([a-z])( +)(\2)( )/i($2) . $3 . ” ” . i($2)/e;
    s/ a /b b/;$e=/^ /;}print;while($_=pop(@l)){print;}

  22. Okay so I’ve thought about my Smalltalk code a bit and here’s an improvement. There’s no recursion in Smalltalk but you can fake it with closures. So my improved Smalltalk example goes like this…

    | chars aBlock ctr |
    aBlock := [ :charToPrint :spacesFromEdge : spacesBetweenChars :printCharTwice |
    spacesFromEdge timesRepeat: [Transcript show: Character space ].
    Transcript show: charToPrint.
    printCharTwice ifTrue: [
    spacesBetweenChars timesRepeat: [Transcript show: Character space ].
    Transcript show: charToPrint.
    ].
    Transcript cr.
    ].
    Transcript clear.
    chars := #($A $B $C $D $E).
    ctr := 0.
    chars do: [: char |
    aBlock value: char value: (chars size) – ctr value: ((ctr * 2)-1) value: (char ~= $A).
    ctr := ctr +1.
    ].
    ctr := ctr – 2.
    chars reverseDo: [:char |
    (char ~= $E) ifTrue:[
    aBlock value: char value: (chars size) – ctr value: ((ctr * 2)-1) value: (char ~= $A).
    ctr := ctr – 1].
    ]

  23. Since we have a few very elaborate submissions here I thought I’d just hop off the verbosity (ie. readability) bandwagon and provide an obfuscated solution in the general purpose language Python:

    a=”ABCDEFGHIJKLMNOPQRSTUVWXYZ”;b=lambda c=[]:[c.append(raw_input(“Which letter? “)),c[-1]if c[-1].isalpha()and len(c[-1])==1 else b()][1].upper();b=a.index(b());a=[b*” “+”A”]+[(b-i)*” “+a[i]+(i*2-1)*” “+a[i] for i in range(1,b+1)];print “\n”.join(a+list(reversed(a))[1:])

    Lord forgive me..

  24. Robert – at least when He forgives you, he’ll also forgive me for the Perl version 🙂

    Anyway, here’s another one, this time in Haskell. Indentation is important in reality, but readability shouldn’t suffer too much. And yes, I went for readability this time!

    import Char

    calcLine :: Int -> Int -> Int -> Int -> String
    calcLine ch col line maxp =
    let tch = if maxp – line == col then [chr ch] else “.” in
    if col == maxp
    then tch
    else tch ++ (calcLine ch (col+1) line maxp) ++ tch

    calcLines :: Int -> Int -> String
    calcLines line maxp =
    let ch = (ord ‘A’) + line in
    let l = (calcLine ch 0 line maxp) ++ “\n” in
    if line == maxp
    then l
    else l ++ (calcLines (line+1) maxp) ++ l

    main = do
    putStrLn “Enter a single letter”
    input <- getLine
    let maxp = (ord (head input)) – (ord ‘a’)
    putStrLn (calcLines 0 maxp)

  25. Overlly verbose version using iterators (sure that spacing logic could be reduced down!)

    static char input;
    const int AsciiA=65;

    static void Main(string[] args)
    {
    Console.WriteLine(“Please enter a character”);
    input = Console.ReadKey().KeyChar;
    Console.WriteLine(“”);

    int MaxWidth = Convert.ToInt32(input) – AsciiA;
    int LeftSpace = MaxWidth + 1;
    int Line = -1;

    //forward rows
    foreach (char c in GetCharForward())
    {
    PrintSpaces(LeftSpace);
    LeftSpace -= 1;

    if(c!=’A’)
    {
    Console.Write(c);
    PrintSpaces((2 * Line));
    Console.Write(c + “\n”);
    }
    else
    {
    Console.WriteLine(c);
    }

    Line += 1;
    }

    MaxWidth = MaxWidth * 2;
    Line = 2;

    //Back rows
    foreach (char c in GetCharBackward())
    {
    MaxWidth -= 2;
    PrintSpaces(Line);

    Console.Write(c);

    if (c == ‘A’) break;

    PrintSpaces(MaxWidth – 2);
    Console.Write(c + “\n”);
    Line += 1;
    }

    Console.ReadKey();
    }

    public static void PrintSpaces(int NumSpaces)
    {
    string space = ” “;

    for (int x = 0; x != NumSpaces; x++)
    {
    space += ” “;
    }

    Console.Write(space);
    }

    public static IEnumerable GetCharForward()
    {
    int current = AsciiA;

    while (Convert.ToChar(current-1) != input)
    {
    yield return Convert.ToChar(current);
    current += 1;
    }
    }

    public static IEnumerable GetCharBackward()
    {
    int current = Convert.ToInt32(input-1);

    while (Convert.ToChar(current + 1) != 65)
    {
    yield return Convert.ToChar(current);
    current -= 1;
    }
    }

  26. Okay, I guess this is my final one… same algorithm as I did in Haskell, re-implemented in Lisp. I was lazy and skipped the part with asking for input and all that – just run something like (output (calcLines 0 4)). The 4 is the maximum depth, i.e. E in this case.

    (defun calcLine (ch col line maxp)
    (let
    ((tch (if (= col (- maxp line)) (cons ch nil) (cons 46 nil))))
    (if (= col maxp) tch (append (append tch (calcLine ch (+ col 1) line maxp)) tch))
    )
    )

    (defun calcLines (line maxp)
    (let*
    ((ch (+ line (char-int #\A)))
    (l (append (calcLine ch 0 line maxp) (cons 10 nil)))
    )
    (if (= line maxp) l (append (append l (calcLines (+ line 1) maxp)) l))
    )
    )

    (defun output (l)
    (dolist (x l)
    (format t “~A” (int-char x)))
    )

  27. With C# and recursion

    public void CodeChallenge(char currentChar, char startChar, char endChar)
    {
    const int maxPad = 30;
    string currentLine;

    if( currentChar == startChar )
    {
    currentLine = currentChar.ToString().PadLeft( maxPad );
    }
    else
    {
    currentLine =
    currentChar.ToString().PadLeft( maxPad – currentChar + startChar ) +
    currentChar.ToString().PadLeft( 2 * (currentChar – startChar) );
    }

    Console.WriteLine(currentLine);

    if(currentChar != endChar)
    {
    CodeChallenge( (char)(currentChar + 1), startChar, endChar );
    Console.WriteLine( currentLine );
    }
    }

  28. Here is my solution in Scala:

    import java.text.Format
    import Math.abs

    object Diamond {

    val A = ‘A’

    private def printDiamond( ch: Char ) {

    def spacingFuncFactory(maxDifference: Int, padding: Int): Int = (2 * (maxDifference – padding)) – 1

    val maxDiff = (ch – A) + 1
    val spacing = spacingFuncFactory(maxDiff, _: Int)

    def outputLine( padding: Int, ch: Int, spaces: Int) {
    def padLeft( count: Int ): String = if( count == 0 ) “” else String.format(“%1$#” + count + “s”, Array(“”))
    val c = ch.asInstanceOf[Char]
    println( padLeft(padding) + c + padLeft(spaces) + (if( spaces > 0 ) c else “”) );
    }

    def print( terminatingValue: Int, diff: Int, diffIncrement: Int, c: Int, cIncrement: Int) {
    if( diff != terminatingValue ) {
    val absoluteDiff = Math.abs(diff)
    outputLine( absoluteDiff, c, spacing(absoluteDiff) )
    print( terminatingValue, diff + diffIncrement, diffIncrement, c + cIncrement, cIncrement )
    }
    }

    print( 0, maxDiff, -1, A, 1)
    print( maxDiff + 1, 2, 1, (ch – 1), -1)
    }

    def main( args: Array[String] ) {
    if (args.length == 0 || args(0).length == 0 ) {
    println(“Diamond: No argument supplied”)
    return
    }
    val firstChar = args(0)(0)
    if( firstChar ‘Z’) {
    println(“Diamond: The first character of the argument supplied is outside of the range of A to Z”)
    return
    }
    printDiamond( firstChar )
    }
    }

  29. Dammit! looks like the blog engine has stripped out some stuff. The last “if” statement is checking to make sure the first char is between “A” and “Z”.
    Cheers
    Barry

  30. Did a SQL 2008 one that can take any char string

    DECLARE @Input AS VARCHAR(MAX)
    DECLARE @Result AS VARCHAR(MAX)
    DECLARE @Length AS INT
    DECLARE @x AS INT

    SET @Input = ‘ABCDE’
    SET @Length = LEN(@Input)
    SET @Result = ”
    SET @x = 0

    WHILE ( @x 0 )
    SELECT @Result = @Result + SPACE(( @x * 2 ) – 1) + SUBSTRING(@Input, @x + 1, 1)

    SELECT @Result = @Result + CHAR(13) + CHAR(10)
    SET @x = @x + 1
    END

    SET @x = @x – 1

    WHILE ( @x >= 0 )
    BEGIN
    SET @x = @x – 1
    SELECT @Result = @Result + SPACE(@Length – @x) + SUBSTRING(@Input, @x + 1, 1)
    IF ( @x > 0 )
    SELECT @Result = @Result + SPACE(( @x * 2 ) – 1) + SUBSTRING(@Input, @x + 1, 1)

    SELECT @Result = @Result + CHAR(13) + CHAR(10)
    END

    PRINT @Result

  31. A clojure version implemented here. Unlike some of you, I went for legibility over terseness. Despite this it’s still only 44 lines of code (including function docs) of clear concise clojure:

    http://paste.lisp.org/display/78674

    It’s also 100% thread safe, easily parallelisable (though to do so would doubtless incure more overhead than it’s worth). The program has no mutable state and has been written in a purely functional style, with only the function to print having a side effect.

  32. I’m afraid my APL-Solution may not display right (although Unicode, so it should)
    (Using Dyalog APL with IO=1 and ML=0):
    {{{⍵⍪1 0↓⊖⍵}⍵,0 ¯1↓1⌽⌽⍵}↑(⎕a⍳⍵){(⍳⍺)⌽(⍺,⍺)↑(⍺,1)⍴⍵}⎕a}’E’

    see screenshot here: http://twitpic.com/3ecda

    BTW: should I win, I have a friend in UK whose postal address you could use to mail the shirt 🙂

  33. Oh, and to have it loop over a set of characters, we just use the “each”-operator:
    {{{⍵⍪1 0↓⊖⍵}⍵,0 ¯1↓1⌽⌽⍵}↑(⎕a⍳⍵){(⍳⍺)⌽(⍺,⍺)↑(⍺,1)⍴⍵}⎕a}¨’BAAS’

    🙂

    Oh no, that last bit was no APL!

  34. Ok I have to represent the Sybase SQL users out there…

    CREATE PROCEDURE DBA."sp_generate_diamond" ( IN apex_char CHAR(1) )
    BEGIN

        DECLARE OFFSET INTEGER;

        DECLARE DISTANCE INTEGER;

        DECLARE DIRECTION INTEGER;

        DECLARE CURRENTLINE CHAR(150);

        SET OFFSET = ASCII(apex_char)-ASCII(‘A’);
        SET DISTANCE = 0;

        SET DIRECTION = 1;

        WHILE ( DISTANCE >= 0 ) LOOP

            SET CURRENTLINE = REPEAT( ‘ ‘, OFFSET-DISTANCE ) || CHAR(ASCII(‘A’)+DISTANCE);

            IF( DISTANCE > 0 ) THEN

              SET CURRENTLINE = CURRENTLINE || REPEAT( ‘ ‘, (DISTANCE*2)-1 ) || CHAR(ASCII(‘A’)+DISTANCE);

            END IF;

            MESSAGE CURRENTLINE TO CLIENT;

            IF( DISTANCE = OFFSET ) THEN

              SET DIRECTION = -1;

            END IF;

            SET DISTANCE = DISTANCE + DIRECTION;

        END LOOP

    END

    Call it using the following statement:-
    CALL dba.sp_generate_diamond( ‘E’ );

    And the code is displayed in the “Statistics” window in Interactive SQL

    Incase of formatting mess see this… http://pastebin.com/m41d6ea23

  35. I was amazed by how many lines of code some languages require to solve this. The whole problem can be done in a single line of APL. I decided to document one solution on the APL Wiki web site:

    http://aplwiki.com/Studio/LetterDiamonds

    (This solution would only need 42 characters if you choose single-letter variable names)

  36. Wot no Ruby?


    diff = ARGV.shift[0].ord - ?A
    [(0..diff),(0...diff).to_a.reverse].each do |range|
    range.each do |i|
    s = (" "*(diff-i)) + (?A+i).chr + (" "*i)
    puts s + s[0..-2].reverse
    end
    end

    Not elegant in any sense, but it works.

  37. Wimps here it is for the Apple II lovingly assembled with the miniassembler if you paste this into the applewin emulator:

    CALL -151
    300:A0 0E B9 00 03 F0 17 20
    :ED FD C8 4C 02 03 C5 CE
    :D4 C5 D2 A0 D3 C9 DA C5
    :A0 C1 AD DA BF 00 20 0C
    :FD C9 C1 30 04 C9 DA B0
    :09 20 2D FF 20 8E FD 4C
    :00 03 29 1F AA CA 86 08
    :A2 FF 86 07 E8 E8 86 09
    :A9 C1 85 06 20 8E FD A6
    :08 D0 06 C6 09 C6 09 D0
    :03 20 4A F9 A5 06 20 ED
    :FD C9 C1 F0 0A A6 07 20
    :4A F9 A5 06 20 ED FD 20
    :8E FD 38 A5 08 E5 09 85
    :08 18 A5 07 65 09 18 65
    :09 85 07 18 A5 06 65 09
    :85 06 C9 C0 D0 C1 60
    300g

    You’ll get a loverly prompting diamond printer.

  38. Here is a version in J: (see http://www.jsoftware.com)

    firstletters=: (65}.a.)&(>:@i. {. [)
    shape=: {.&>~ # {. i:@# NB. shape letters
    hflipcat=: ,. |.@}:”1 NB. flip horizontally & catenate
    vflipapp=: , |.@}: NB. flip vertically & append

    vflipapp hflipcat shape firstletters ‘E’

    Or as one function (verb):
    diamond=: vflipapp@hflipcat@shape@firstletters
    diamond ‘E’

    If anyone wanted it as one line of code:
    (,|.@}:)(,.|.@}:”1)({.&>~#{.i:@#)(65}.a.)(>:@i.{.[)’E’

  39. Note that the blog is “smart” and converts single and double quotes into “pretty” quotes.

    As a result if you copy the code back to a J session you will need to replace the “pretty” versions with their simple counterparts for the code to work.

  40. Here’s an APL2 solution that is not a one-liner.
    (I like comments better than brevity.)

    ∇LETTERDIAMOND[⎕]∇

    [0] MAT←LETTERDIAMOND ARG;LIST;⎕IO
    [1] ⎕IO←0 ⍝ Set the index origin
    [2] LIST←⎕AF(⎕AF ‘A’)+0,⍳-/⎕AV⍳ARG,’A’ ⍝ Build list of letters
    [3] MAT←⊃(-⍳⍴LIST)↑¨LIST ⍝ Build upper right part
    [4] MAT←(⌽MAT),1↓[1]MAT ⍝ Add upper left part
    [5] MAT←MAT,[0]1↓[0]⊖MAT ⍝ Add lower half
    ∇ 2009-04-16 22.50.50 (GMT-4)

    LETTERDIAMOND ‘E’

    A
    B B
    C C
    D D
    C C
    B B
    A

    BTW This sample is encoded using Unicode codepoints for the APL characters. If you can not read it here, I suggest you copy and
    paste it into an editor and use Adrian Smith’s APL385 Unicode
    font which is available here: http://www.vector.org.uk/?area=fonts.

    David Liebtag
    IBM APL Products and Services

Comments are closed.