loading...

Daily Challenge #52 - Building a Pyramid

thepracticaldev profile image dev.to staff ・1 min read

Challenge
Write a function that will draw a pyramid. The function should take one integer parameter that will describe how many lines tall the pyramid should be.

This challenge is simple, but requires a bit of reflection to get it right. Here is an example in Node.js hosted on Repl.it.

Example
pyramid(5);

Good luck!


This challenge comes from @aminnairi here on DEV! Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Discussion

pic
Editor guide
 

Fun challenge :) I did it in React, so here's the function:

  const pyramid = count => {
    return Array(count).fill().map((_, n) => {
      return <span key={n}>
        {Array((n + 1)*2 - 1).fill().map(i => {
          return "*"
        })}
      </span>
    })
  }

and here's a demo!

 

Python one-liner yet again :)

>>> pyramid = lambda x: print('\n'.join(f'{a}' for a in [' '*(((2*(x-i)-1)//2))+'*'*(i*2+1) for i in range(x)]))
>>> pyramid(5)
    *
   ***
  *****
 *******
*********
>>> pyramid(4)
   *
  ***
 *****
*******
>>> pyramid(1)
*
 
 

I have no Go pun this time. Sorry, I need coffee for that.

pyramid.go

package pyramid

// Pyramid creates a text pyramid of the given height
func Pyramid(height int) string {
    if height <= 0 {
        return ""
    }

    // Determine width of pyramid base
    width := (height * 2) - 1

    // Get middle of pyramid
    middle := int(width / 2)
    row := 0

    // Initialize a base row with only spaces
    base := make([]rune, width, width)
    for i := range base {
        base[i] = ' '
    }

    // Set the top row 'block'
    base[middle] = '*'

    var pyramid string

    // Build the pyramid starting from the top
    for row < height {
        base[middle+row] = '*'
        base[middle-row] = '*'
        pyramid += string(base) + "\n"

        row++
    }

    return pyramid
}

pyramid_test.go

package pyramid

import "testing"

func TestPyramid(t *testing.T) {
    testCases := []struct {
        description string
        input       int
        expected    string
    }{
        {
            "small pyramid",
            3,
            "  *  \n *** \n*****\n",
        },
        {
            "no height",
            0,
            "",
        },
        {
            "negative height",
            -3,
            "",
        },
        {
            "big pyramid",
            7,
            "      *      \n     ***     \n    *****    \n   *******   \n  *********  \n *********** \n*************\n",
        },
    }

    for _, test := range testCases {
        if result := Pyramid(test.input); result != test.expected {
            t.Fatalf("FAIL: %s - Pyramid(%d): %s, expected: %s", test.description, test.input, result, test.expected)
        }
        t.Logf("PASS: %s", test.description)
    }
}

 

Since this is pretty much the top half of a string diamond I took my answer from that challenge and used just the first half of the code but it isn't working like expected, can someone tell me where I went wrong? 😥

Ruby:

def riamond_i_mean_ryramid(karat)
  unless karat.odd? && karat > 0
    raise ArgumentError.new("You want a #{karat} karat ~~diamond~~ pyramid? That's crazy!")
  end
  cut = 1
  layers = []
  until cut > karat
    layers.push(("*" * cut).center(karat))
 

Here it is!

// Minified
f=(a)=>[...Array(a)].map((_,i)=>(c=' '.repeat(a-i),`${c}${'*'.repeat(2*i+1)}${c}`)).join`\n`

// Readable
const pyramid = (input) => {
  return [...Array(input)].map((_, index) => {
    const padding = ' '.repeat(input - index);
    const stars = '*'.repeat(2 * index + 1);
    return `${padding}${stars}${padding}`;
  }).join('\n');
}

The right padding isn't necessary, but hey why not

'\n' + pyramid(10)
"
          *          
         ***         
        *****        
       *******       
      *********      
     ***********     
    *************    
   ***************   
  *****************  
 ******************* "
 

My try. I used padStart and padEnd, and abused default parameters a bit, just for fun.

const PYRAMID = "*";
const AIR = "·";

const pyramid = (n, {maxWidth = (n * 2) - 1, leftPadding = maxWidth / 2} = {}) => 
    Array.from({length: n}, (_, idx, __, {width = (( idx + 1 ) * 2) - 1} = {}) => 
        PYRAMID.repeat(width)
            .padStart(idx + leftPadding + 1, AIR)
            .padEnd(maxWidth, AIR)
    ).join("\n");
 

CSS

This is a variation of my solution for challenge #2. Just add the class "triangle" to a block element, and specify the lines in the CSS variable --stars:

.triangle {
  --stars: 10;
  width: calc(2 * var(--stars) * 10px);
  height: calc((var(--stars)) * 15px);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="10px" height="15px" viewBox="0 0 10 15"><text x="5" y="12" fill="black" dominant-baseline="middle" text-anchor="middle">*</text></svg>');
  background-position: top center;
  clip-path: polygon(50% 0, 100% 100%, 0% 100%);
}

The idea is having an inline SVG background with the asterisks, then clipping the shape of the triangle. Here is a demo on Codepen:

 
const pyramid = n => Array(n).fill(1)
    .map((_, lineno) => lineno * 2 + 1)
    .map(x => [x, (n * 2 - 1 - x) / 2])
    .map(([x, y]) => [Array(x).fill('*').join(''), Array(y).fill(' ').join('')])
    .map(([x, y]) => `${y}${x}${y}`).join('\n');
 

F#:

let pyramid (x: int) =
  let print maxWidth count =
    (String.replicate (maxWidth - count) " ") + (String.replicate (2*count-1) "*")

  let pr = (x - 1) * 2 + 1 |> print

  for i in [1..x] do
     i |> pr |> printfn "%s"
 

My solution in js

const padSpaceToSides = (str, len) => {
  const padlen = len - str.length,
    right = Math.ceil(padlen / 2),
    left = padlen - right;
  return str = Array(left + 1).join(' ') + str + Array(right + 1).join(' ');
},
pyramid = (height, atom = '*') => {
  const start = 1,
    step = 2;
  for(let i = start; i < height * 2; i += step) {
    console.log(padSpaceToSides(atom.repeat(i), height * 2 - 1));
  }
};

pyramid(10)

Output

 

Javascript, can be run from the command line:

const floors = Number(process.argv[2]);

for (let currentFloor = 1; currentFloor <= floors; currentFloor += 1) {
  console.log(
    '*'.repeat(2 * currentFloor - 1)
      .padStart((floors + currentFloor - 1), ' '),
  );
}
 
def pyramid(lines):
  for line in range(1, 1 + lines * 2, 2):
    print(' ' * (lines - line // 2 - 1) + '*' * line)
 
var pyramid = (height) => {
  var pyramidLayer = '*';
  width = height * 2;
  for(var i=1;i<width;i++) {
     if(i % 2 === 1) {
        console.log(pyramidLayer.padStart((width+i)/2, ' '));
    }
    pyramidLayer += '*';
  }
}
pyramid(5);
 
function pyramid(i) {
    for( k = 1; i != 0; k++) {
        console.log(" ".repeat(--i) + "*".repeat(k*2-1));
    }
}
 

Python

def pyramid(line):
    if line == 1:   print('*')
    elif line == 0: print('')
    else:
        for i in range(line):
            print(' '*(line-i-1),'*'*(2*i+1))
 

This is really close to challenge #2

 

Rust:

pub fn pyramid(floors: usize) -> String {
    (0..floors)
        .map(|f| " ".repeat(floors - f - 1) + &"*".repeat(2 * f + 1))
        .collect::<Vec<_>>()
        .join("\n")
}
 
 

Isn't that a triangle?