24
\$\begingroup\$

Introduction

Yesterday I saw a birthday puzzle. Congrats!!

Also this week I watched an episode of the TV show Bones where a dead body was found buried under a tree. To calculate the time of death, they counted the tree rings.

Tree rings form because trees grow slower during the winter and faster during the summer. Thus you can calculate the tree's age by counting the rings. Also you can see natural events like rainy or dry seasons.

enter image description here

Challenge

Given an integer n >= 1 as input, write a full program to output the tree age rings.

Because rings can change of shape use three diferent characters ('0', '*', '+') to show climate cycles.

Age 1

0

Age 2

***
*0*
***

Age 3

+++++
+***+
+*0*+
+***+
+++++

Age 4

0000000
0+++++0
0+***+0
0+*0*+0
0+***+0
0+++++0
0000000

Size of the tree is a square of sides 2*n - 1

Winning

Shortest code in bytes wins.

\$\endgroup\$
  • \$\begingroup\$ What about when age=5? \$\endgroup\$ – Blue Sep 9 '15 at 17:14
  • 3
    \$\begingroup\$ the rings have a three steps cycle.('0', '*', '+') so 5 year is * \$\endgroup\$ – Juan Carlos Oropeza Sep 9 '15 at 17:15
  • \$\begingroup\$ @vihan don't understand the question. \$\endgroup\$ – Juan Carlos Oropeza Sep 10 '15 at 2:15
  • \$\begingroup\$ @vihan sorry still dont understand how divide by two solve the problem. If you have a hack to solve it, i probably dont know about it \$\endgroup\$ – Juan Carlos Oropeza Sep 10 '15 at 2:17
  • \$\begingroup\$ Is the size the area, the perimeter or the length of each side? \$\endgroup\$ – Beta Decay Sep 10 '15 at 6:11

16 Answers 16

6
\$\begingroup\$

K5, 27 30 26 25 22 bytes

"0"{4(|+y,)/x}/"0*+"3!1_!

This approach iteratively "wraps" a core (beginning with "0") on all four sides using some other character ({4(|+y,)/x}). The sequence of seasonal wrappings is determined by a modulo 3 (3!) sequence. It's a bit fiddly to get the base case to line up just right.

edit:

"0*+"3!u|\:u:t,1_|t:|!

This alternative builds the entire rectangular array at once from the provided exclusive range (!) reversed and joined with itself after dropping an item (t,1_|t:|). We then take the cartesian product maximum (u|\:u:), take the entire matrix modulo 3 (3!) and index into the array of characters.

In action:

  "0*+"3!u|\:u:t,1_|t:|!1
,,"0"

  "0*+"3!u|\:u:t,1_|t:|!3
("+++++"
 "+***+"
 "+*0*+"
 "+***+"
 "+++++")

  "0*+"3!u|\:u:t,1_|t:|!5
("*********"
 "*0000000*"
 "*0+++++0*"
 "*0+***+0*"
 "*0+*0*+0*"
 "*0+***+0*"
 "*0+++++0*"
 "*0000000*"
 "*********")
\$\endgroup\$
  • \$\begingroup\$ I don't know K, but is this a full program and not just a function? \$\endgroup\$ – Alex A. Sep 9 '15 at 18:27
  • \$\begingroup\$ It is both a full program and a function. It's an example of what is called a "tacit definition". The distinction is extremely arbitrary anyway. \$\endgroup\$ – JohnE Sep 9 '15 at 19:10
11
\$\begingroup\$

BBC Basic, 93 bytes

1I.r:r=r-1:F.i=-r TOr:F.j=-r TOr:p=ABS(i):q=ABS(j):IFp<q TH.p=q
2V.48-(p MOD3)*6MOD7:N.:P.:N.

The abbreviated keywords help out a lot here. In line 2, I'm using the VDU command (equivalent to C's putchar()) to print each character. This is a lot more efficient than P.MID$("0*+",p MOD3+1,1).

Here it is running in BeebEm3 on a Mac:

enter image description here

\$\endgroup\$
  • \$\begingroup\$ How you create that gif? \$\endgroup\$ – Juan Carlos Oropeza Sep 10 '15 at 16:45
  • 9
    \$\begingroup\$ @JuanCarlosOropeza Not very efficiently. I used QuickTime Player to capture the screen, QuickTime Player 7 to export the video to PNG images, GraphicConverter to turn them into a GIF, and ezgif.com to optimize the results. \$\endgroup\$ – squeamish ossifrage Sep 10 '15 at 18:17
7
\$\begingroup\$

CJam, 25 bytes

q~,_1>W%\+_ff{e>"0*+"=}N*

Test it here.

Explanation

q~,       e# Read input N, turn into range [0 1 ... N-1]
_1>       e# Duplicate and cut off the zero.
W%        e# Reverse.
\+        e# Prepend to original range to give [N-1 ... 1 0 1 ... N-1]
_         e# Duplicate
ff{       e# Nested map for each pair of elements in that array.
  e>      e# Take the maximum, i.e. chessboard distance from the centre.
  "0*+"=  e# Select the right character using cyclic indexing into this string.
}
N*        e# Join the lines with line feeds.
\$\endgroup\$
5
\$\begingroup\$

Matlab, 63 bytes

n=input('')-1;x='0*+';t=abs(-n:n);x(mod(bsxfun(@max,t,t'),3)+1)

Example:

>> n=input('')-1;x='0*+';t=abs(-n:n);x(mod(bsxfun(@max,t,t'),3)+1)
5
ans =
*********
*0000000*
*0+++++0*
*0+***+0*
*0+*0*+0*
*0+***+0*
*0+++++0*
*0000000*
*********
\$\endgroup\$
5
\$\begingroup\$

Python 2, 83 bytes

I=n=input()
while I+n-1:I-=1;i=abs(I);w=("O*+"*n)[i:n];print w[::-1]+w[0]*2*i+w[1:]

Prints line by line. Each line is chopped into three parts:

  • The left cycling part, including the first repeated char.
  • The repeating center part
  • The right cycling part.

For n=4:

0    000000    
0+    ++++    0
0+*    **    +0
0+*0        *+0
0+*    **    +0
0+    ++++    0
0    000000    

We generate the left part in reverse as w, clone its last character 2*i times, then add on the original version without the first character.

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

Python 2, 83 bytes

n=input()
R=range(1-n,n)
for i in R:print''.join('0*+'[max(i,-i,j,-j)%3]for j in R)

If we think of the tree as a coordinate grid, the symbol at (i,j) is determined by max(abs(i),abs(j))%3, or equivalently max(i,-i,j,-j)%3. For each row i, we join and print the symbols in that row.

\$\endgroup\$
  • \$\begingroup\$ You could shorten this by putting the range statement straight in the third line. \$\endgroup\$ – Ethan Brouwer Sep 9 '15 at 23:34
  • \$\begingroup\$ @EthanBrouwer I'm using R twice, and it's longer than 5 chars, so assignment wins out. \$\endgroup\$ – xnor Sep 9 '15 at 23:40
  • \$\begingroup\$ touche! I only saw the first one. My bad. :) \$\endgroup\$ – Ethan Brouwer Sep 10 '15 at 2:49
5
\$\begingroup\$

Pyth, 23 bytes

VK+_StQUQsm@"0*+"eS,dNK

Try it online: Demonstration

Explanation:

VK+_StQUQsm@"0*+"eS,dNK   implicit: Q = input number
    StQ                   the list [1, 2, ..., Q-1]
   _                      reverse it [Q-1, ..., 2, 1]
       UQ                 the list [0, 1, ..., Q-1]
  +                       combine them [Q-1, ..., 1, 0, 1, ..., Q-1]
 K                        and store in K
VK                        for each N in K:
          m           K      map each element d in K to:
                 eS,dN          the maximum of d and N
           @"0*+"               and pick the corresponded char (modulo 3)
         s                   join the chars to a string and print
\$\endgroup\$
3
\$\begingroup\$

MATLAB, 80 78 73 bytes

Thanks Luis Mendo for helping me shave 5 bytes!

A=eye(2*input('')-1);a='0*+';a(mod(bwdist(A.*rot90(A),'chessboard'),3)+1)

Example

>> A=eye(2*input('')-1);a='0*+';a(mod(bwdist(A.*rot90(A),'chessboard'),3)+1)

5

ans =

*********
*0000000*
*0+++++0*
*0+***+0*
*0+*0*+0*
*0+***+0*
*0+++++0*
*0000000*
*********

Ungolfed and Code Explanation

%// Accepts an integer n from the user and creates a 2*n - 1 x 2*n - 1 identity matrix
A=eye(2*input('')-1);

%// Creates an array of three characters to print each level of the ring
a='0*+';

%// By taking the identity matrix and element-wise multiplying with its 90 degree rotated 
%// version of itself, this creates a zero matrix except for the centre most
%// value, which is 1
%// This takes the distance transform via the chessboard / Chebyshev distance
%// from the centre element
%// This mirrors what "level" each square would be at
%// 1: https://en.wikipedia.org/wiki/Distance_transform
%// 2: https://en.wikipedia.org/wiki/Chebyshev_distance
b = bwdist(A.*rot90(A),'chessboard');

%// Because each level cycles through each of the characters in the
%// character array a, we need to perform a mod operation so that
%// all of the values cycle from 1 to 3
%// This changes the distance transform output so that we range
%// from 1 to 3 instead
c = mod(b,3) + 1;

%// The values in the matrix c correspond exactly to the locations
%// we need to sample from the array a and we display our result
a(c)

Minor Note

bwdist is a function that's part of the image processing toolbox, and can only be run in MATLAB. Octave (IIRC) does not have bwdist implemented yet so this can't be run in Octave.

\$\endgroup\$
  • \$\begingroup\$ You can save a few bytes: use eye and multiply element-wise by its rot90'ed version to generate the "seed" matrix: I=eye(2*input('')-1);a='0*+';a(mod(bwdist(I.*rot90(I),'chessboard'),3)+1) \$\endgroup\$ – Luis Mendo Sep 10 '15 at 11:38
  • \$\begingroup\$ Oh cool! Thanks @LuisMendo \$\endgroup\$ – rayryeng Sep 10 '15 at 12:22
2
\$\begingroup\$

Python 2, 134 bytes

def l(x,c=1):
 p="\n\x1b[%d"%c;d=p+";%dH"%c
 if x:s=x*2-1;d+=(p+"G").join(["0*+"[(x+1)%3]*s]*s)+l(x-1,c+1)
 return d
print l(input())
\$\endgroup\$
2
\$\begingroup\$

Perl, 118 bytes

More to do, but a basic version for now. Now with delicious extra spec adherence.

for$i(0..($-=<>-1)){substr$a[$_],$i,$}=2*($--$i)+1,(0,'*','+')[($--$i)%3]x$}for$i..$-}$,=$/;print@a,reverse@a[0..$--1]

Usage:

perl -e 'for$i(0..($-=<>-1)){substr$a[$_],$i,$}=2*($--$i)+1,(0,'*','+')[($--$i)%3]x$}for$i..$-}$,=$/;print@a,reverse@a[0..$--1]' <<< 9
+++++++++++++++++
+***************+
+*0000000000000*+
+*0+++++++++++0*+
+*0+*********+0*+
+*0+*0000000*+0*+
+*0+*0+++++0*+0*+
+*0+*0+***+0*+0*+
+*0+*0+*0*+0*+0*+
+*0+*0+***+0*+0*+
+*0+*0+++++0*+0*+
+*0+*0000000*+0*+
+*0+*********+0*+
+*0+++++++++++0*+
+*0000000000000*+
+***************+
+++++++++++++++++
\$\endgroup\$
1
\$\begingroup\$

Matlab 92

input('')-1;x=ones(2*n+1,1)*abs(-n:n);z=mod(max(x,x'),3);z(z>1)=2;z(z<1)=7;disp([z+41,''])
\$\endgroup\$
1
\$\begingroup\$

Sed, 277 252 characters

(251 character code + 1 character command line option.)

Expects input in unary format.

:m
s/1/0/
s/1/*/
s/1/+/
tm
h
s/^/:/
:r
s/(.*):(.)/\2\1:/
tr
s/://
G
s/\n.//
h
:
/^(.)\1*$/ba
s/(.)(.)(\2*)\1/\1:\2\3:\1/
:c
s/(:_*)[^_](.*:)/\1_\2/
tc
:u
s/(.)(:\1*)_/\1\2\1/
tu
s/://g
H
b
:a
g
s/[^\n]+/:/
:f
s/(.*):(\n[^\n]+)/\2\1:/
tf
s/://
G
s/\n//

Sample run:

bash-4.3$ sed -rf treering.sed <<< 1
0

bash-4.3$ sed -rf treering.sed <<< 11
***
*0*
***

bash-4.3$ sed -rf treering.sed <<< 111
+++++
+***+
+*0*+
+***+
+++++

bash-4.3$ sed -rf treering.sed <<< 1111
0000000
0+++++0
0+***+0
0+*0*+0
0+***+0
0+++++0
0000000
\$\endgroup\$
0
\$\begingroup\$

JavaScript (ES6), 114

Using alert for output - bad proportional font and the result is ugly. In the snippet below the alert is redirect to the snipped body, giving a better result. The newline inside backticks is significant and counted.

Test running the snippet in Firefox.

/* Redefine alert for testing purpose */ alert=x=>O.innerHTML=x;

[...'*+0'.repeat(n=prompt()-1)].map((c,i)=>i<n?b=[z=c.repeat(i-~i),...b,z].map(r=>c+r+c):0,b=[0]);alert(b.join`
`)
<pre id=O></pre>

\$\endgroup\$
  • \$\begingroup\$ I try run the code snippet, but nothing happen. Dont know if that is because I open stack overflow in chrome? \$\endgroup\$ – Juan Carlos Oropeza Sep 10 '15 at 16:46
  • \$\begingroup\$ @JuanCarlosOropeza maybe that. I have written: Test running the snippet in Firefox but obviously I was just kidding, Chrome (no version of Chrome) is not EcmaScritpt 6 compliant, missing the => functions. \$\endgroup\$ – edc65 Sep 10 '15 at 17:49
  • \$\begingroup\$ @JuanCarlosOropeza I have to correct myself. The newest release of Chrome has the arrow function, but does not understand the spread operator .... Still far from ES6 \$\endgroup\$ – edc65 Sep 13 '15 at 14:04
0
\$\begingroup\$

Ruby, 85 characters

puts (m=0..(n=gets.to_i-1)*2).map{|i|m.map{|j|"0*+"[[(i-n).abs,(j-n).abs].max%3]}*""}

Sample run:

bash-4.3$ ruby -e 'puts (m=0..(n=gets.to_i-1)*2).map{|i|m.map{|j|"0*+"[[(i-n).abs,(j-n).abs].max%3]}*""}' <<< 4
0000000
0+++++0
0+***+0
0+*0*+0
0+***+0
0+++++0
0000000
\$\endgroup\$
0
\$\begingroup\$

Moonscript - 104 bytes

m=io.read!
n=2*m-1
for y=1,n
 io.write ({'0','*','+'})[(math.max y-m,x-m,m-y,m-x)%3+1]for x=1,n
 print!
\$\endgroup\$
0
\$\begingroup\$

C, 138 bytes

j,k,l;t(i){l=2*i;char*c=calloc(l,l);memset(c,10,l*(l-2));for(;k<i;++k)for(j=k;j<l-1-k;)memset(c+j++*l+k,"+0*"[(i-k)%3],l-2*k-1);puts(c);}

Function t taking one integer parameter - the age.

Ungolfed (with main function to easily run the above one):

#include "stdlib.h" /* calloc - only necessary for 64-bit system */
j,k,l;t(i)
{
    l=2*i;
    char*c=calloc(l,l);
    memset(c,10,l*(l-2)); /* fill with '\n' */
    for(;k<i;++k)for(j=k;j<l-1-k;)memset(c+j++*l+k,"+0*"[(i-k)%3],l-2*k-1);
    puts(c);
}

main(int c,char**v)
{
    t(atoi(v[1]));
}

The stdlib.h may be necessary on some systems, because without it the return type of the undeclared function calloc would default to int. Because int and char* are not necessarily of same size, an invalid pointer could be written into c. In most 32-bit systems both char* and int have same size, but this is not true for 64-bit systems.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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