Code Golf: Beehive

2019-03-08 02:31发布

问题:

The challenge

The shortest code by character count that will generate a beehive from user input.

A beehive is defined a a grid of hexagons in a size inputted by the user as two positive numbers greater than zero (no need to validate input). The first number (W) represents the width of the beehive - or - how many hexagons are on each row. The second number (H) represents the height of the beehive - or - how many hexagons are on each column.

A Single hexagon is made from three ASCII characters: _, / and \, and three lines:

 __
/  \
\__/

Hexagons complete each other: the first column of the beehive will be 'low', and the second will be high - alternating and repeating in the same pattern forming W hexagons. This will be repeated H times to form a total of WxH hexagons.

Test cases:

Input:
    1 1
Output:
     __
    /  \
    \__/

Input:
    4 2
Output:
        __    __
     __/  \__/  \
    /  \__/  \__/
    \__/  \__/  \
    /  \__/  \__/
    \__/  \__/

Input:
    2 5
Output:
        __ 
     __/  \
    /  \__/
    \__/  \
    /  \__/
    \__/  \
    /  \__/
    \__/  \
    /  \__/
    \__/  \
    /  \__/
    \__/

Input:
    11 3
Output:
        __    __    __    __    __
     __/  \__/  \__/  \__/  \__/  \__
    /  \__/  \__/  \__/  \__/  \__/  \
    \__/  \__/  \__/  \__/  \__/  \__/
    /  \__/  \__/  \__/  \__/  \__/  \
    \__/  \__/  \__/  \__/  \__/  \__/
    /  \__/  \__/  \__/  \__/  \__/  \
    \__/  \__/  \__/  \__/  \__/  \__/

Code count includes input/output (i.e full program).

回答1:

Perl, 99 characters

@P=map{$/.substr$".'__/  \\'x99,$_,$W||=1+3*pop}0,(3,6)x pop;
chop$P[0-$W%2];print"    __"x($W/6),@P

Last edit: Saved one character replacing -($W%2) with 0-$W%2 (thanks A. Rex)

Explanation:

For width W and height H, the output is 2+2 * H lines long and 3 * W+1 characters wide, with a lot of repetition in the middle of the output.

For convenience, we let $W be 3 * W + 1, the width of the output in characters.

The top line consists of the pattern " __", repeated W/2 == $W/6 times.

The even numbered lines consist of the repeating pattern "\__/ ", truncated to $W characters. The second line of output is a special case, where the first character of the second line should be a space instead of a \.

The odd numbered lines consist of the repeating pattern "/ \__", truncated to $W characters.

We construct a string: " " . "__/ \" x 99. Note that the beginning of this string is the desired output for the second line. This line starting at position 3 is the desired output for the odd lines, and starting at position 6 for the even numbered lines.

The LIST argument to the map call begins with 0 and is followed by H repetitions of (3,6). The map call creates a list of the substrings that begin at the appropriate positions and are $W = 3 * W + 1 characters long.

There is one more adjustment to make before printing the results. If W is odd, then there is an extra character on the second line ($P[0]) that needs to be chopped off. If W is even, then there is an extra character on the bottom line ($P[-1]) to chop.



回答2:

Python 2.6 - 144 characters including newlines

I can save about 20 more characters if the inputs are allowed to be comma separated.

C,R=map(int,raw_input().split())
print C/2*"    __"+"\n "+("__/  \\"*99)[:3*C-C%2]
r=0
exec'r+=3;print ("\__/  "*99)[r:r+3*C+1-r/6/R*~C%2];'*2*R

The version that takes input from the command line is 4 more bytes:

import sys
C,R=map(int,sys.argv[1:])
print C/2*"    __"+"\n "+("__/  \\"*99)[:3*C-C%2]
r=0
exec'r+=3;print ("\__/  "*99)[r:r+3*C+1-r/6/R*~C%2];'*2*R


回答3:

C89 (136 characters)

x;y;w;main(h){for(h=scanf("%d%d",&w,&h)*h+2;y++
<h;++x)putchar(x>w*3-(y==(w&1?2:h))?x=-1,10:
"/  \\__"[--y?y-1|x?(x+y*3)%6:1:x%6<4?1:5]);}


回答4:

Perl, 160 characters

$w=shift;for$h(-1..2*shift){push@a,join'',(('\__','/  ')x($w+$h))[$h..$w+$h]}
$a[0]=~y#\\/# #;$a[1]=~s/./ /;s/_*$//for@a;$a[$w%2||$#a]=~s/. *$//;print$_,$/for@a

No cleverness involved at all: just fill the array with characters, then weed out the ones that look ugly.

strager's masterpiece is only 137 characters when ported to Perl, but all credit should go to him.

$w=shift;$\=$/;for$y(1..($h=2+2*shift)){print map+(split//,'_ \__/  ')
[$y-1?$y-2|$_?($_+$y%2*3)%6+2:1:$_%6<4],0..$w*3-!($w&1?$y-2:$y-$h)}


回答5:

J, 143 characters

4(1!:2)~(10{a.)&,"1({.4 :0{:)".(1!:1)3
|:(18,(}:,32-+:@{:)3 3 8 1 1 10$~3*x){(,' '&(0})"1,' '&(0 1})"1)(,}."1)(}."1,}:"1)(3++:y)$"1'/\',:' _'
)

Using J feels very awkward when dealing with variable-length strings and the sort of console-oriented user interaction that is assumed in other languages. Still, I guess this is not too bad...

Stealing ideas once more (J is much easier to work with once you find a way of looking at the problem in an array-structured way), here's mobrule's masterpiece ported in 124 (ick, it's longer than the original):

4(1!:2)~({.4 :0{:)".(1!:1)3
(x}~' '_1}(x=.-1-+:2|x){])((10{a.),(' ',,99#'__/  \',:'    __'){~(i.>:3*x)+])"0]595 0,3 6$~+:y
)


回答6:

C#, 216 characters

class B{static void Main(string[]a){int b=0,i=0,w=int.Parse(a[0])+1,z=2*w*(int.Parse(a[1])+1);for(;i<z;b=(i%w+i/w)%2)System.Console.Write("\\/ "[i>w&(w%2>0?i<z-1:i!=2*w-1)?b>0?0:1:2]+(++i%w<1?"\n":b>0?"__":"  "));}}

Less obfuscated:

class B{
    static void Main(string[]a){
       int b=0,
           i=0,
           w=int.Parse(a[0])+1,
           z=2*w*(int.Parse(a[1])+1);

       for(;i<z;b=(i%w+i/w)%2)
           System.Console.Write(
             "\\/ "[i>w&(w%2>0?i<z-1:i!=2*w-1)?b>0?0:1:2]
             +
             (++i%w<1?"\n":b>0?"__":"  ")
           );
    }
}

I used the following method:

input: 4 2
cols:  0 00 1 11 2 22 3 33 4 44     
row 0:" |  | |__| |  | |__| |"
    1:" |__|/|  |\|__|/|  |\|"
    2:"/|  |\|__|/|  |\|__|/|"
    3:"\|__|/|  |\|__|/|  |\|"
    4:"/|  |\|__|/|  |\|__|/|"
    5:"\|__|/|  |\|__|/|  | |"
  1. Iterate from zero to (W+1)*(H*2+1). The *2 is because each comb is 2 lines tall, and +1 to account for the first line and end of lines.
  2. Render two "pieces" of a hexagon per iteration:
  3. Decide between " ", "\", and "/" for the first part
  4. Decide between "__", "  ", and "\n" for the second part

The pattern is evident if you look at a large enough honeycomb. Half the logic is there only to address exceptions in the first row, the end of the second row, and the last cell.



回答7:

Ruby, 164

$ ruby -a -p bh.rb

strager's masterpiece in Ruby...

w,h = $F; w=w.to_i
(1..(h = h.to_i * 2 + 2)).each { |y|        
  (0...(w * 3 + (y != ((w & 1) != 0 ? 2 : h) ? 1:0))).each { |x|
    $> << ('_ \__/  ' [
      y - 1 != 0 ?
        (y - 2 | x) != 0 ?
          (x + y % 2 * 3) % 6 + 2 : 1 : (x % 6 < 4) ? 1:0]).chr
  }
  $> << $/
}

aka

w,h=$F;w=w.to_i
(1..(h=h.to_i*2+2)).each{|y|(0...(w*3+(y!=((w&1)!=0?2:h)?1:0))).each{|x|$><<('_ \__/  '[y-1!=0?(y-2|x)!=0?(x+y%2*3)%6+2:1:(x%6<4)?1:0]).chr}
$><<$/}


回答8:

NewLisp: 257 chars

I'm sure this is not an optimal solution:

(silent(define(i v)(println)(set v(int(read-line))))(i'w)(i'h)(set't(+(* 3 w)1))(set'l " __/ \\__/ ")(define(p s e(b 0))(println(slice(append(dup" "b)(dup(s 6 l)w))0 e)))(p 0 t)(p 4(- t(% w 2))1)(dotimes(n(- h 1))(p 6 t)(p 9 t))(p 6 t)(p 9(- t(%(+ w 1)2))))

Less obfuscated:

(silent
  (define (i v)
          (println)
          (set v (int (read-line))))
  (i 'w)
  (i 'h)
  (set 't (+ (* 3 w) 1))
  (set 'l "    __/  \\__/  ")
  (define (p s e (b 0))
          (println (slice (append (dup " " b) (dup (s 6 l) w)) 0 e)))
  (p 0 t)
  (p 4 (- t (% w 2)) 1)
  (dotimes (n (- h 1))
    (p 6 t)
    (p 9 t))
  (p 6 t)
  (p 9 (- t(% (+ w 1)2))))

I'm sure I could write the loop differently and save two lines and a few characters, for instance, but it's late...



回答9:

Golfscript, 88 characters

Based on the mobrule's solution. It was a lot of work to get it smaller than that one! Newlines are just for clarity.

~:r;:c 3*):W 6/"    __"*n
[][0]r[3 6]*+{[" ""__/  \\"99*+>W<]+.},;
c 2%-1 1if:r%)[-1<]+r%
n*

Explanation:

~:r;,:c              # read input into rows, columns
3 *):W               # store c*3+1 into W
6 /"    __"*n        # write out "    __" W/6 times, plus newline
[]                   # initialize output array
[0]r[3 6]*+          # create array [0] + [3,6] repeated r times
{                    # for every entry in the input array...
[" ""__/  \\"99*+    #   create the magic string
>W<                  #   truncate it between [n:W], where n is the cur entry
]+                   #   store this line in the output array
.},;                 # repeat for every entry in array
                     # now to handle the extra cases:
c 2%-1 1if:r%        # reverse the array if c is odd, do nothing if it's even
)[-1<]               # take the last entry in the array, cut off the last char
+r%                  # put it back on the array, and un-reverse it
n*                   # now join the array with newlines


Here is my original entry at 118 characters:

Late entry, but it's 2nd smallest! (I'm just using these to learn Golfscript). Newlines are for clarity.

~:r;:c 2%:o;c 2/:b"    __"*n:e
{e" ""\\"if"__/  \\"b*o{"__"e{"":e}"/"if}{"":e}if n
"/""  \\__/"b*o"  \\"""if n}r*
"\\__/  "b o+*


回答10:

C89 - 261 necessary chars

All white spaces can be removed. My solution uses rotation of the board...

x,y,W,h,B[999],*a,*b,*c,*d;
main(w){
  for(scanf("%d%d",&h,&w);y<h;y++,*b++ = *c++ = 63)
    for(x=0,
        W=w*2+2-(h==1),
        a=B+y*W*3+y%2,
        b=a+W,
        c=b+W,
        d=c+W;x++<w;)

      *a++ = 60,
      *a++ = *d++ = 15,
      *b++ = *c++ = 63,
      *b++ = *c++ = 0,
      *d++ = 60;

  for(x=W;--x>=0;puts(""))
    for(y=0;y<h*3+1;putchar(B[x+y++*W]+32));
}


回答11:

C# 377 chars

Didn't want to disappoint anyone waiting for the "funny" C# answer. Unfortunately, it's not 250 lines though...;)


using System;
class P{
    static void Main(string[] a){
        int i,j,w=Int32.Parse(a[0]),h=Int32.Parse(a[1]);
        string n="\n",e="",o=e,l="__",s=" ",r=s+s,b=@"\",f="/";
        string[] t={r+r,l,b+l+f,r,l,f+r+b,e,f,b,s};
        for(i=0;i<w;)o+=t[i++%2];
        for(i=0;i<2*h;i++){
            o+=n+(i%2==0?i!=0?b:s:e);
            for(j=0;j<w;)
                o+=t[((j+++i)%2)+4];
            o+=i!=0?t[((w+i)%2)+6]:e;
        }
        o+=n;
        for(i=0;i<w;)o+=t[i++%2+2];
        Console.Write(o);
    }
}



回答12:

F#, 303 chars

let[|x;y|]=System.Console.ReadLine().Split([|' '|])
let p=printf
let L s o e=p"%s"s;(for i in 1..int x do p"%s"(if i%2=1 then o else e));p"\n"
if int x>1 then L" ""  "" __ ";L" ""__""/  \\"
else L" ""__"""
for i in 1..int y-1 do(L"/""  \\""__/";L"\\""__/""  \\")
L"/""  \\""__/"
L"""\\__/""  "

EDIT

Now that there are finally some other answers posted, I don't mind sharing a less-obfuscated version:

let [|sx;sy|] = System.Console.ReadLine().Split([|' '|])
let x,y = int sx, int sy

let Line n start odd even =
    printf "%s" start
    for i in 1..n do
        printf "%s" (if i%2=1 then odd else even)
    printfn ""

// header
if x > 1 then
    Line x " "   "  "   " __ "
    Line x " "   "__"   "/  \\"
else    
    Line x " "   "__"   "    "

// body
for i in 1..y-1 do
    Line x "/"    "  \\"   "__/"
    Line x "\\"   "__/"    "  \\"

// footer
Line x "/"   "  \\"    "__/"
Line x ""    "\\__/"   "  "


回答13:

Groovy, #375 chars

Same logic & code that @markt implemented in c#, but have changed few places for Groovy :)

public class FunCode {
        public static void main(a) {
            int i,j,w=Integer.parseInt(a[0]),h=Integer.parseInt(a[1]);
            String n="\n",e="",o=e,l="__",s=" ",r=s+s,b="\\",f="/";
            def t=[r+r,l,b+l+f,r,l,f+r+b,e,f,b,s];
            for(i=0;i<w;)o+=t[i++%2];
            for(i=0;i<2*h;i++){
                o+=n+(i%2==0?i!=0?b:s:e);
                for(j=0;j<w;)
                    o+=t[((j+++i)%2)+4];
                o+=i!=0?t[((w+i)%2)+6]:e;
            }
            o+=n;
            for(i=0;i<w;)o+=t[i++%2+2]; println(o);
        }
    }


回答14:

Lua, 227 characters

w,h,s=io.read("*n"),io.read("*n")*2+2," " for i=1,h do b=(i%2>0 and "/  \\__" or "\\__/  "):rep(w/2+1):sub(1,w*3+1) print(i==1 and b:gsub("[/\\]",s) or i==2 and b:gsub("^\\",s):gsub("/$",s) or i==h and b:gsub("\\$",s) or b) end

208 characters, when width and height are read from command line.

s,w,h=" ",... h=h*2+2 for i=1,h do b=(i%2>0 and "/  \\__" or "\\__/  "):rep(w/2+1):sub(1,w*3+1) print(i==1 and b:gsub("[/\\]",s) or i==2 and b:gsub("^\\",s):gsub("/$",s) or i==h and b:gsub("\\$",s) or b) end