26
\$\begingroup\$

You know those stackable shelves that are basically just wooden boxes that can be stacked together? We're going to simulate building some bookshelves out of those with some ASCII art.

Our books are all conveniently uniform in size, and all look like the following:

|X|
|X|
|X|

The bookshelves are individual boxes, always three characters high on the inside (enough to fit a book standing upright), composed of | characters on the left and right, - characters for the top and bottom, and wide enough to fit X books (where X is an input integer). For example, here's a bookshelf of size 3:

|---------|
|         |
|         |
|         |
|---------|

because you can fit 3 books into it like so

|---------|
||X||X||X||
||X||X||X||
||X||X||X||
|---------|

The input is going to be two strictly positive integers, X and Y, where X is the width of the shelves we have (measured in books), and Y is how many books we have to stack. If we have more books than fit on a single shelf, we need to add more shelves to the top. For example, here is input 4 wide / 6 books:

|------------|
||X||X|      |
||X||X|      |
||X||X|      |
|------------|
|------------|
||X||X||X||X||
||X||X||X||X||
||X||X||X||X||
|------------|

If Y % X > 0, meaning the number of books is not an integer multiple of the shelf size, the remainder books should go on the top-most left-most position (as in the case with 4 6, above) and the remaining part of that shelf filled in with spaces.

Input

  • Two strictly positive integers in any convenient format, each >0.
  • You can take the input in either order (e.g., size of shelves first, then number of books, or vice versa). Please state in your submission the input order.
  • You can safely assume that neither input will be larger than your language's default [int] size (or equivalent).

Output

The resulting ASCII art representation of the books and bookshelves.

Rules

  • Leading or trailing newlines or whitespace are all optional, so long as the characters themselves line up correctly.
  • Either a full program or a function are acceptable. If a function, you can return the output rather than printing it.
  • If possible, please include a link to an online testing environment so other people can try out your code!
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.

Further Examples

6 wide / 2 books
|------------------|
||X||X|            |
||X||X|            |
||X||X|            |
|------------------|

2 wide / 6 books
|------|
||X||X||
||X||X||
||X||X||
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|

4 wide / 9 books
|------------|
||X|         |
||X|         |
||X|         |
|------------|
|------------|
||X||X||X||X||
||X||X||X||X||
||X||X||X||X||
|------------|
|------------|
||X||X||X||X||
||X||X||X||X||
||X||X||X||X||
|------------|
\$\endgroup\$
2
  • \$\begingroup\$ Can I make it so the shelf with the least amount of books is on the bottom, so like it fills top to bottom \$\endgroup\$ Commented Mar 3, 2017 at 11:32
  • 1
    \$\begingroup\$ @GoldenRatio No, the books must be filled bottom to top, left to right. \$\endgroup\$ Commented Mar 3, 2017 at 13:18

19 Answers 19

14
\$\begingroup\$

JavaScript (ES6), 100 99 98 bytes

Takes the width w and the number of books b in currying syntax (w)(b).

w=>g=(b,s=`|${'-'.repeat(w*3)}|
`,r=s.replace(/---/g,_=>b&&b--?'|X|':'   '))=>(b?g(b)+s:s)+r+r+r+s

Formatted and commented

w =>                                // main function: takes width 'w' as input, returns 'g'
  g = (                             // g = recursive function with:
    b,                              //   - b = number of books
    s = `|${'-'.repeat(w * 3)}|\n`, //   - s = top/bottom of shell, filled with '-'
    r = s.replace(                  //   - r = pattern of the current row of books,
      RegExp('---', 'g'),           //         using 's' as a template and updating
      _ => b && b-- ? '|X|' : '   ' //         'b' while building it
    )                               // NB: 'r' must be defined in the scope of 'g',
  ) =>                              //     otherwise it would be overwritten by
    (                               //     subsequent calls
      b ?                           // if there are remaining books:
        g(b) + s                    //   do a recursive call and append shell top
      :                             // else:
        s                           //   just append shell top
    ) + r + r + r + s               // append book rows and shell bottom

Test cases

let f =

w=>g=(b,s=`|${'-'.repeat(w*3)}|
`,r=s.replace(/---/g,_=>b&&b--?'|X|':'   '))=>(b?g(b)+s:s)+r+r+r+s

console.log(f(6)(2))
console.log(f(2)(6))
console.log(f(4)(9))

\$\endgroup\$
9
\$\begingroup\$

Bash (+utilities), 130, 108, 106 bytes

A single, continuous, shell pipeline to render your bookshelves.

Changelog:

  • Optimized first sed expression a bit, -12 bytes (Thx @Riley !)
  • Replaced printf + seq with a raw printf, -10 bytes
  • Refactored the second sed expression, -2 bytes

Golfed

printf %$2s\\n|fold -$1|sed "s/ /|X|/g;:;/.\{$[$1*3]\}/!s/$/ /;t;h;s/./-/gp;x;p;p;p;x"|sed 's/.*/|&|/'|tac

$./shelf 6 8
|------------------|
||X||X|            |
||X||X|            |
||X||X|            |
|------------------|
|------------------|
||X||X||X||X||X||X||
||X||X||X||X||X||X||
||X||X||X||X||X||X||
|------------------|

Try It Online !

How It Works

$./shelf 2 3

printf %$2s\\n - generate n whitespace characters, one per book (shown as _)

___

fold -$1 - fold them by the shelf length

__
_

sed "s/ /|X|/g;" - replace _ with X, add book covers

|X||X|
|X|

:;/.\{$[$1*3]\}/!s/$/ /;t - right pad with spaces (shown as _)

|X||X|
|X|___

h;s/./-/gp;x;p;p;p;x - triplicate each line, and add --- before and after it.

------
|X||X|
|X||X|
|X||X|
------
------
|X|   
|X|   
|X|   
------

sed 's/.*/|&|/'|tac - wrap lines in | |, reverse with tac

|------|
||X|   |
||X|   |
||X|   |
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|
\$\endgroup\$
2
  • \$\begingroup\$ In the first sed you can use a nameless label, and t instead of b so you don't need the {}. You can skip the s/./-/g because they are already -s. Try it Online! \$\endgroup\$
    – Riley
    Commented Mar 2, 2017 at 14:12
  • \$\begingroup\$ @Riley That's an excellent advice, thank you ! \$\endgroup\$
    – zeppelin
    Commented Mar 2, 2017 at 15:02
6
\$\begingroup\$

Python 2, 133 113 105 bytes

I'm sure there's a better way...

X,Y=input()
k='|'+'---'*X+'|'
while Y:g=Y%X or X;print k+'\n'+('|'+'|X|'*g+'   '*(X-g)+'|'+'\n')*3+k;Y-=g

Input is taken width, books

-20 bytes thanks to @ovs for noticing an unnecessary lambda function!
-8 bytes thanks to @ovs for shortening the input.

\$\endgroup\$
4
  • \$\begingroup\$ X,Y=input() is a shorter way to take the to values. \$\endgroup\$
    – ovs
    Commented Mar 1, 2017 at 22:24
  • \$\begingroup\$ @ovs Oh wait, I put that there for my first try. Whoops. Nice catch, thanks! \$\endgroup\$
    – hyperneutrino
    Commented Mar 2, 2017 at 3:03
  • 1
    \$\begingroup\$ @ovs Thanks, so then input is taken as X, Y, right? \$\endgroup\$
    – hyperneutrino
    Commented Mar 2, 2017 at 14:55
  • 2
    \$\begingroup\$ I think that you can save two bytes by defining '|' as a variable. \$\endgroup\$ Commented Mar 2, 2017 at 18:09
6
\$\begingroup\$

Batch, 261 bytes

@set/an=~-%1%%%2+1,b=%1-n
@set s=
@set t=
@for /l %%i in (1,1,%2)do @call set t=---%%t%%&if %%i gtr %n% (call set s=%%s%%   )else call set s=%%s%%X
@for %%s in ("|%t%|" "|%s:X=|X|%|" "|%s:X=|X|%|" "|%s:X=|X|%|" "|%t%|")do @echo %%~s
@if %b% gtr 0 %0 %b% %2

Uses my trick from my Batch answer to Let's play tennis to easily print lots of | characters.

\$\endgroup\$
5
\$\begingroup\$

Haskell, 100 bytes

x#y returns the string for width x and y books.

s?n=[1..n]>>s
x#y|x<y=x#(y-x)++x#x|w<-"---"?x,b<-"|X|"?y++"   "?(x-y)=[w,b,b,b,w]>>=('|':).(++"|\n")

Try it online!

The main function/operator is #. When x<y it splits the books into y-x and x, then recurses. When x>=y, w and b are the two line types, minus the outer |s and the newline.

The helper operator s?n concatenates n copies of the string s.

\$\endgroup\$
5
\$\begingroup\$

PowerShell, 149 134 bytes

param($w,$b)$s="|$('-'*$w*3)|"
if($a=$b%$w){,$s+,"|$('|X|'*$a)$(' '*3*($w-$a))|"*3+$s}
if($b-=$a){(,$s+,"|$('|X|'*$w)|"*3+$s)*($b/$w)}

Try it online!

Takes input $width and $books. Sets string $s to be one of the horizontal shelves. Then we have two if statements.

The first checks whether we have "remainder" books. If so, we output the shelf, the (number of books plus number of spaces)*3, and another shelf.

Next, we see if we still have books remaining after removing the remainders ($a). Same sort of setup, except we're using $w number of books. Since at this point, $b is guaranteed to be a multiple of $w (because we removed the remainder, $a), we don't need to worry about rounding.

Removed the [math]::Floor() call, saving 15 bytes

All of these strings are left on the pipeline, and implicit Write-Output happens at program completion.

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

CJam, 62 61 bytes

q~1a*W$/W$f{0e]}{{"|X|"S3*?}%s__'-3*W$*_}%1m>W%"|
|"*"||"\*o;

Takes input as width books

Try it online!

Explanation

q~           Read and eval input (pushes width W and books B to the stack)
1a*          Push an array containing  the number 1 B times
W$/          Split it into chunks of size W
W$f{0e]}     Pad each chunk to width W by adding 0's to the right (the last chunk might be 
              shorter than W)
{            Apply the following to each chunk:
 {            Apply the following to each number in the chunk:
  "|X|"S3*?    Push "|X|" if the number is 1, or "   " if it's 0
 }%           (end of block)
 s            Stringify (joins with no separator)
 __           Duplicate twice (each shelf is 3 identical lines)
 '-3*W$*_     Push a string containing '-' repeated 3×W times, then duplicate it
}%           (end of block)
              At this point we have an array containing sequences of 3 identical lines 
              each followed by two lines of -'s
1m>          Rotate the array 1 to the right; brings the final line of -'s to the start
W%           Reverse the array, so that the top shelf is the partially empty one
"|\n|"*      Join the array with the string "|\n|", to build the sides of the shelves
"||"\*       Join the string "||" with the shelf string (adds the first and last | chars)
o            Print the result
;            Pop and discard W
\$\endgroup\$
4
\$\begingroup\$

Python 3, 142 bytes

Still working on it. b is for 'number of books' and w is for shelf width.

def s(b,w):
 R=b%w
 B='|\n'
 I='|'
 X='|X|'
 d=I+3*w*'-'+B
 f=I+X*w+B
 p=I+R*X+3*(w-R)*' '+B
 print(R and d+3*p+d or" ")+b//w*(d+3*f+d))
\$\endgroup\$
6
  • \$\begingroup\$ Welcome to PPCG! This doesn't work for me unless R=b%w is moved down to the next line. Also, you should be able to remove the whitespace around those three = to save some bytes. \$\endgroup\$ Commented Mar 2, 2017 at 16:46
  • \$\begingroup\$ Welcome to PPCG! \$\endgroup\$ Commented Mar 2, 2017 at 16:57
  • \$\begingroup\$ You can replace d+3*p+d if R!=0 else '' with R and d+3*p+d or'' \$\endgroup\$
    – shooqie
    Commented Mar 3, 2017 at 14:52
  • \$\begingroup\$ @shooqie I'm curious, how could this evaluate to the result of d+3*p+d? \$\endgroup\$ Commented Mar 4, 2017 at 10:42
  • 1
    \$\begingroup\$ You can save a few bytes by putting all the definitions in one line using semicolons. \$\endgroup\$
    – L3viathan
    Commented Apr 21, 2017 at 19:13
3
\$\begingroup\$

AHK, 208 bytes

AutoTrim,Off
w=%1%
b=%2%
f:=Mod(b,w)
Loop,%w%
s=%s%---
s=|%s%|`n
If (f>0) {
Loop,%f%
t=%t%|X|
Loop,% w-f
t=%t% ` ` `
t=|%t%|`n
t:=s t t t s
}
Loop,%w%
r=%r%|X|
r=|%r%|`n
Loop,% (b-f)/w
t:=t s r r r s
Send,%t%

There are a few things frustating me from golfing further:

  • AutoHotkey doesn't have a built-in repeat function
  • You can't directly use the passed in arguments (%1% & %2%) in math functions because those expect variable or number input and it will assume the unescaped 1 to be the number one rather than the variable name
  • I am not very good at golfing

An easier to read version of the above looks like this:

AutoTrim,Off
w=%1%
b=%2%
f:=Mod(b,w)

Loop,%w%
   s=%s%---
s=|%s%|`n

If (f>0) {
   Loop,%f%
      t=%t%|X|
   Loop,% w-f
      t=%t% ` ` `
   t=|%t%|`n
   t:=s t t t s
}

Loop,%w%
   r=%r%|X|
r=|%r%|`n

Loop,% (b-f)/w
   t:=t s r r r s

Send,%t%

If a Loop doesn't use brackets {}, then only the next line is part of the loop. If setting a variable's value using := instead of =, you can drop the percent sign escape characters. Tilde n is the newline character.

\$\endgroup\$
3
\$\begingroup\$

Java 7, 230 224 222 bytes

String c(int w,int b){String r="",n="|\n",z="|";int i=0,j,k,t=b%w<1?w:b%w,x=b/w+(t!=w?1:0);for(;i++<w;z+="---");z+=n;for(i=0;i<x;i++){r+=z;for(j=0;j++<3;r+=n){r+="|";for(k=0;k<w;r+=i<1&k++>=t?"   ":"|X|");}r+=z;}return r;}

Explanation:

String c(int w, int b){                // Method with two integer parameters and String return-type
  String r = "",                       //  The return-String
         n = "|\n",                    //  Part that's used multiple times in the code
         z = "|";                      //  Shelf part of the book-boxes
  int i = 0, j, k,                     //  Indexes used in the for-loops
      t = b%w < 1 ? w : b%w,           //  Books on top shelf
      x = b/w + (t != w ? 1 : 0);      //  Amount of shelves
  for(; i++ < w; z += "---"); z += n;  //  Create the shelf-part ("|---|"; with w times "---")
  for(i = 0; i < x; i++){              //  Loop over the rows
    r += z;                            //   Append the result with the shelf-part
    for(j = 0; j++ < 3; ){             //   Loop three times (the height of the books & boxes)
      r += "|";                        //    Append the result-String with "|"
      for(k = 0; k < w;                //    Loop over the columns
          r +=                         //     And append the result-String with:
           i < 1                       //      If this is the first row:
           & k++ >= t ?                //      And the current column is larger or equal to the amount of books in the top shelf
             "   "                     //       Use an empty space
           :                           //      Else:
             "|X|"                     //       Use the book-part
            );                         //    End of columns loop
         r += n;                       //    Append the result-String with a "|" and a new-line
       }                               //   End of the loop of three
      r += z;                          //   Append the result-String with the shelf-part
    }                                  //  End of rows loop
    return r;                          //  Return the result-String
 }                                     // End of method

Test code:

Try it here.

class M{
  static String c(int w,int b){String r="",n="|\n",z="|";int i=0,j,k,t=b%w<1?w:b%w,x=b/w+(t!=w?1:0);for(;i++<w;z+="---");z+=n;for(i=0;i<x;i++){r+=z;for(j=0;j++<3;r+=n){r+="|";for(k=0;k<w;r+=i<1&k++>=t?"   ":"|X|");}r+=z;}return r;}

  public static void main(String[] a){
    System.out.println(c(6, 2));
    System.out.println(c(2, 6));
    System.out.println(c(4, 9));
  }
}

Output:

|------------------|
||X||X|            |
||X||X|            |
||X||X|            |
|------------------|

|------|
||X||X||
||X||X||
||X||X||
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|

|------------|
||X|         |
||X|         |
||X|         |
|------------|
|------------|
||X||X||X||X||
||X||X||X||X||
||X||X||X||X||
|------------|
|------------|
||X||X||X||X||
||X||X||X||X||
||X||X||X||X||
|------------|
\$\endgroup\$
4
  • 1
    \$\begingroup\$ I don't know how, but it looks like lambdas and .repeat really, really help in this challenge. \$\endgroup\$ Commented Oct 25, 2018 at 10:10
  • \$\begingroup\$ @OlivierGrégoire Considering I posted this about about 1.5 year ago, I'm not surprised it can be golfed quite substantially. ;) \$\endgroup\$ Commented Oct 25, 2018 at 10:18
  • \$\begingroup\$ Ow... I hadn't checked the date: I only saw that this question was active and that a full other algorithm for Java was possible. My bad... \$\endgroup\$ Commented Oct 25, 2018 at 10:34
  • \$\begingroup\$ @OlivierGrégoire No problem, and well done with your answer. :) It almost feels nostalgic looking back at this answer when I was still adding the test cases and output to the answer, and answered everything in Java 7, because I didn't understand Java 8 yet. XD \$\endgroup\$ Commented Oct 25, 2018 at 10:52
2
\$\begingroup\$

PowerShell, 109 bytes

param($w,$b)for(;$b;$b-=$c){if(!($c=$b%$w)){$c=$w}($l="|$('-'*$w*3)|")
,"|$('|X|'*$c)$(' '*($w-$c)*3)|"*3
$l}

Less golfed test script:

$f = {

param($w,$b)
for(;$b;$b-=$c){
    if(!($c=$b%$w)){$c=$w}
    ($l="|$('-'*$w*3)|")
    ,"|$('|X|'*$c)$(' '*($w-$c)*3)|"*3
    $l
}

}

@(
    ,(6, 2, 
    "|------------------|",
    "||X||X|            |",
    "||X||X|            |",
    "||X||X|            |",
    "|------------------|")

    ,(2, 6,
    "|------|",
    "||X||X||",
    "||X||X||",
    "||X||X||",
    "|------|",
    "|------|",
    "||X||X||",
    "||X||X||",
    "||X||X||",
    "|------|",
    "|------|",
    "||X||X||",
    "||X||X||",
    "||X||X||",
    "|------|")

    ,(4, 9,
    "|------------|",
    "||X|         |",
    "||X|         |",
    "||X|         |",
    "|------------|",
    "|------------|",
    "||X||X||X||X||",
    "||X||X||X||X||",
    "||X||X||X||X||",
    "|------------|",
    "|------------|",
    "||X||X||X||X||",
    "||X||X||X||X||",
    "||X||X||X||X||",
    "|------------|")
) | % {
    $w,$b,$expected = $_
    $result = &$f $w $b
    "$result"-eq"$expected"
    $result
}

Output:

True
|------------------|
||X||X|            |
||X||X|            |
||X||X|            |
|------------------|
True
|------|
||X||X||
||X||X||
||X||X||
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|
True
|------------|
||X|         |
||X|         |
||X|         |
|------------|
|------------|
||X||X||X||X||
||X||X||X||X||
||X||X||X||X||
|------------|
|------------|
||X||X||X||X||
||X||X||X||X||
||X||X||X||X||
|------------|

PowerShell, 109 bytes, alternative

param($w,$b)for(;$b;$b-=$c){($l="|$('---'*$w)|")
,"|$('|X|'*($c=(($b%$w),$w-ne0)[0]))$('   '*($w-$c))|"*3
$l}
\$\endgroup\$
1
\$\begingroup\$

Python 2, 120 118 bytes

i,j=input()
a=j%i
n='|\n'
x='|'+'---'*i+n
print(x+('|'+'|x|'*a+' '*(i-a)*3+n)*3,'')[a<1]+(x+('|'+'|x|'*i+n)*3)*(j/i)+x

Try it online!

Have been meaning to have a go at this one for the last few days. Now that I have finally got time to do it there's already a shorter Python answer. Oh well, just posted as an alternative.

Input taken as width,books

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

SOGL, 64 bytes

be%→M"Q└ƨS‘*ač;┼→S%‘A |e3* -* |++M?tMSeM-9*@*a+┼Ot}be÷:?{teSa┼Ot

Explanation: First function:

   →M  define function M which pushes
b      the book amount
  %    mod
 e     the bookshelf width

second function:

           →S  create function S (example input: 3)          [3]
"Q└ƨS‘         push the string "|||XXX|||" (the book)        [3, "|||XXX|||"]
      *        multiply by the number on stack (book count)  ["|||XXX||||||XXX||||||XXX|||"]
       a       push variable A (later defined "|||")         ["|||XXX||||||XXX||||||XXX|||", "|||"]
        č      chop into char array                          ["|||XXX||||||XXX||||||XXX|||", ["|", "|", "|"]]
         ;     swap top 2 on stack                           [["|", "|", "|"], "|||XXX||||||XXX||||||XXX|||"]
          ┼    horizontally append                           [["||X||X||X|", "||X||X||X|", "||X||X||X|"]]

this function expects a number (book count) on stack and outputs the bookshelfs books

["||X||X||X|",
 "||X||X||X|",
 "||X||X||X|"]

Further down example given is e=3 (bookshelf width) and b=8 (book amount)

%‘A              var A = "|||"                        
    |            push "|"                      ["|"]                
     e3*         push E * 3                    ["|", 9]             
         -*      push that many "-"es          ["|", "---------"]   
            |+   append "|"                    ["|", "---------|"]  
              +  prepend the "|"               ["|---------|"]      

this is the bookshelf top/bottom line and always stays on the stack first part (half-empty bookshelf)

First main part

M?               }               if the modulo != 0
  tM                             output the bookshelf top/bottom line
    S                            execute the S function width the modulo
     eM-                         push bookshelf width - modulo (empty space count)
        9*                       multiply by 9 (books are 3x3 so 3x3 spaces)
          @*                     get that many spaces
            a+                   append to that "|||"
              ┼                  horizontally append
               O                 output
                t                output the bookshelf top/bottom line

And the last part

be÷            floor divide book amout by width (full shelves)
   :?          if not 0 (a bug makes all loops execute once)
     {         repeat
      t        output the bookshelf top/bottom line
       eS      execute S with shelf width (full shelf)
         a┼    horizontally append "|||"
           O   output
            t  output the bookshelf top/bottom line
\$\endgroup\$
1
\$\begingroup\$

Java (JDK), 124 bytes

s->b->{for(int i=0;i++<(b-~s)/s*5;)System.out.printf("|%-"+s*3+"s|%n",i%5<2?"-".repeat(s*3):"|X|".repeat(i<5&b%s>0?b%s:s));}

Try it online!

Credits

\$\endgroup\$
0
0
\$\begingroup\$

PHP>=7.1, 138 Bytes

for([,$w,$b]=$argv;$i<ceil($b/$w)*5;)echo str_pad("|".str_repeat(["","|X|"][$t=($i+1)%5>1],$i++<5&&$b%$w?$b%$w:$w),$w*3+1,"- "[$t])."|\n";

Online Version

\$\endgroup\$
0
\$\begingroup\$

Canvas, 33 bytes

|X|3*×⁷3×⇵-×|3*×╫│;22╋P
%?%⁸}÷[⁷⁸

Try it here!

Explanation (some characters have been replaced to look more monospace):

|X|3*×⁷3×⇵-×|3*×╫│;22╋P  helper function. Prints a shelf with X books
|X|                      push "|X|"
   3*                    repeat it 3 times vertically
     ×                   repeat that horizontally by the item (X) below on the stack
      ⁷3×                push width * 3
         ⇵               ceiling divide that by 2
          -×             repeat "-" that many times
            |3*          repeat "|" vertically 3 times (aka "|¶|¶|")
               ×         prepend that to the dashes (aka ¼ of a bookshelf)
                ╫│       quad-palindromize with horizontal overlap of the remainder
                           taken before and vertical overlap of 1
                  ;      get the books on top
                   22╋   and at coordinates (2;2) in the shelf, place them in
                      P  print that whole thing

%?%⁸}÷[⁷⁸  
%?  }      if width%amount (!= 0)
  %⁸         execute the helper function with width%amount on the stack
     ÷[    repeat floor(width/amount) times
       ⁷     push width
        ⁸    execute the helper function
\$\endgroup\$
0
\$\begingroup\$

Pip -n, 45 bytes

Wb-:yPPZ['-X3Xa"|X|"X(Yb%a|a).sX3Xa-yRL3]WR'|

Takes the width and book count, respectively, as command-line arguments. Try it online!

Explanation

We run a loop to print the shelves one by one from top to bottom. At each iteration, we update b (the number of books to print) by subtracting y (the number of books printed on that iteration). When b reaches 0, the loop exits.

Wb-:yPPZ['-X3Xa"|X|"X(Yb%a|a).sX3Xa-yRL3]WR'|
                                               a is 1st cmdline arg (shelf width); b is 2nd cmdline
                                                 arg (# books); s is space; y is ""
                                               Note that "" becomes zero in numeric contexts
Wb-:y                                          While b decremented by y is nonzero:
                       b%a|a                    b mod a, or a if that quantity is zero
                      Y                         Yank that value into y
                     (      )                   This is the number of books on the current shelf
               "|X|"                            Book-spine string
                    X                           Repeated y times
                                  a-y           Number of empty slots on the current shelf
                              sX3X              Three spaces for each slot
                             .                  Concatenate to the book-spines string
                                     RL3        Make a list of 3 copies of that string
         '-X3Xa                                 3*a hyphens
        [                               ]       Put that string and the above list in a list
                                         WR'|   Wrap all strings in the nested list in |
      PZ                                        Palindromize the outer list (adding a copy of the
                                                hyphens to the end of it)
     P                                          Print, joining all sublists on newlines (-n flag)

Because that's a little involved, here's an example of the first iteration when a = 3, b = 8:

Yb%a|a       2
"|X|"X ^     "|X||X|"
^ .sX3Xa-y   "|X||X|   "
^ RL3        ["|X||X|   ";"|X||X|   ";"|X||X|   "]
['-X3Xa ^ ]  ["---------";["|X||X|   ";"|X||X|   ";"|X||X|   "]]
^ WR'|       ["|---------|";["||X||X|   |";"||X||X|   |";"||X||X|   |"]]
PZ ^         ["|---------|";["||X||X|   |";"||X||X|   |";"||X||X|   |"];"|---------|"]

which then prints as

|---------|
||X||X|   |
||X||X|   |
||X||X|   |
|---------|
\$\endgroup\$
0
\$\begingroup\$

Pyth, 56 bytes

M++GHGV_fTs*V,]Q1.DEQjCg*5\|smgL\-*L3?d"|X|""   ".[*]1N0

Accepts shelf width, book count as separate arguments in that order. Try it online here, or verify all the test cases at once here.

M++GHGV_fTs*V,]Q1.DEQjCg*5\|smgL\-*L3?d"|X|""   ".[*]1N0Q   Implicit: Q=1st arg, E=2nd arg
                                                            Trailing Q inferred
M                                                           Define a function, g(G,H):
 ++GHG                                                        Return G + H + G
                 .DEQ                                       Divmod E by Q, yields [E//Q, E%Q]
             ,]Q1                                           [[Q], 1]
           *V                                               Vectorised multiply the two previous results
                                                              This yields Q repeated E//Q times, then E%Q
          s                                                 Flatten
        fT                                                  Filter out falsey values (i.e. trailing 0 if present)
       _                                                    Reverse (to put partially filled shelf on top)
      V                                                     For N in the above:
                                                    ]1        [1]
                                                   *  N       Repeat the above N times
                                                 .[    0Q     Pad the above on the right with 0, to length Q
                             m                                Map the above, as d, using:
                                     ?d"|X|""   "               If d != 0, yield "|X|", else "   "
                                  *L3                           Multiply each char by 3
                                                                  Yields ['|||','XXX','|||'] or ['   ','   ','   ']
                              gL\-                              Use g to wrap each element in '-'
                            s                                 Flatten
                       g*5\|                                  Use g to add '|||||' to start and end of the above
                      C                                       Transpose
                     j                                        Join on newlines, implicit print
\$\endgroup\$
0
\$\begingroup\$

Forth (gforth), 622 bytes (minimized (remove comments, indentation, 1-char word names) to 303 bytes)

My first play with Forth :)

: bar 124 EMIT ;

: delimline ( width -- )
    bar
    3 * 0 DO 45 EMIT LOOP
    bar CR
;

: bookline ( width books -- )
    bar
    DUP 0 DO bar 88 EMIT bar LOOP
    2DUP = IF
        DROP DROP
    ELSE
        - 0 do 3 SPACES LOOP
    THEN
    bar CR
;

: shelf ( width books -- )
    DUP 0 = IF
        DROP DROP
    ELSE
        OVER delimline
        3 0 DO OVER OVER bookline LOOP
        DROP delimline
    THEN
;

: stack ( width books -- )
    CR
    OVER OVER OVER MOD shelf
    OVER /
    DUP 0 = IF
        DROP DROP
    ELSE 
        0 DO DUP DUP shelf LOOP
    THEN
;

6 2 stack
2 6 stack
3 5 stack
4 4 stack

Try it online!

Output


|------------------|
||X||X|            |
||X||X|            |
||X||X|            |
|------------------|

|------|
||X||X||
||X||X||
||X||X||
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|
|------|
||X||X||
||X||X||
||X||X||
|------|

|---------|
||X||X|   |
||X||X|   |
||X||X|   |
|---------|
|---------|
||X||X||X||
||X||X||X||
||X||X||X||
|---------|

|------------|
||X||X||X||X||
||X||X||X||X||
||X||X||X||X||
|------------|
\$\endgroup\$

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.