每日ASCII艺术#2-流蛇


32

流蛇,也称为高斯帕曲线,是分形曲线,用一个简单的过程的每个顺序/迭代中大小呈指数增长。以下是有关构造的详细信息以及各种订单的一些示例:

订单1流蛇

____
\__ \
__/

2级流蛇

      ____
 ____ \__ \
 \__ \__/ / __
 __/ ____ \ \ \
/ __ \__ \ \/
\ \ \__/ / __
 \/ ____ \/ /
    \__ \__/
    __/

3级流蛇

                 ____
            ____ \__ \
            \__ \__/ / __
            __/ ____ \ \ \    ____
           / __ \__ \ \/ / __ \__ \
      ____ \ \ \__/ / __ \/ / __/ / __
 ____ \__ \ \/ ____ \/ / __/ / __ \ \ \
 \__ \__/ / __ \__ \__/ / __ \ \ \ \/
 __/ ____ \ \ \__/ ____ \ \ \ \/ / __
/ __ \__ \ \/ ____ \__ \ \/ / __ \/ /
\ \ \__/ / __ \__ \__/ / __ \ \ \__/
 \/ ____ \/ / __/ ____ \ \ \ \/ ____
    \__ \__/ / __ \__ \ \/ / __ \__ \
    __/ ____ \ \ \__/ / __ \/ / __/ / __
   / __ \__ \ \/ ____ \/ / __/ / __ \/ /
   \/ / __/ / __ \__ \__/ / __ \/ / __/
   __/ / __ \ \ \__/ ____ \ \ \__/ / __
  / __ \ \ \ \/ ____ \__ \ \/ ____ \/ /
  \ \ \ \/ / __ \__ \__/ / __ \__ \__/
   \/ / __ \/ / __/ ____ \ \ \__/
      \ \ \__/ / __ \__ \ \/
       \/      \ \ \__/ / __
                \/ ____ \/ /
                   \__ \__/
                   __/

施工

考虑要使用包含7条边和8个顶点的路径构建1阶Flow Snake(如下所示。为方便起见,对其进行了放大):

4____5____6
 \         \
 3\____2   7\
       /
0____1/

现在,对于每个下一个订单,您只需将边缘替换为该原始订单1模式的旋转版本。使用以下3条规则替换边缘:

1对于水平边缘,将其替换为原始形状,如下所示:

________
\       \
 \____   \
     /
____/

2对于/边缘(12在上述结构中),请用以下旋转版本替换它:

 /
/   ____
\  /   /
 \/   /
     /
____/

3对于\边缘(3467以上),将其替换为以下旋转版本:

 /
/   ____ 
\   \   \
 \   \   \
  \  /
   \/

因此,例如,带有标记为1阶的顶点的2阶看起来像

            ________
            \       \
  ________   \____   \6
  \       \      /   /
   \____   \5___/   /   ____
       /            \   \   \
  4___/   ________   \   \   \7
 /        \       \   \  /
/   ____   \____   \2  \/
\   \   \      /   /
 \   \   \3___/   /   ____
  \  /            \  /   /
   \/   ________   \/   /
        \       \      /
         \____   \1___/
             /
        0___/

现在,对于更高的阶数,您只需将当前电平分解为长度为/1,1 \或2的边_并重复该过程即可。请注意,即使在替换后,任意两个连续边之间的公共顶点仍然重合。

挑战

  • 您必须编写一个完整程序的函数,该函数可以N通过STDIN / ARGV / function参数或最接近的等效参数接收单个整数,并N在STDOUT上打印Flow Snake 的顺序。
  • 输入整数始终大于0
  • 不应有任何不属于模式的前导空格。
  • 应该没有尾随空格或足够的尾随空格以填充图案以完全填充最小边界矩形。
  • 尾随换行符是可选的。

有趣的事实

  • Flow Snakes是Snow Flakes的文字游戏,此模式类似于2阶及更高阶
  • 实际上,“流和蛇”在模式中起着一定的作用,因为模式是由一条贯穿整个路径的路径组成的。
  • 如果您仔细注意,则2阶(以及更高阶)图案包括1阶图案的旋转,这些旋转在当前和上一个边的公共顶点上枢转。
  • 流蛇有一个非ASCII变体,可以在此处和其他几个位置找到。

这是因此最短的代码以字节为单位!


排行榜

该系列的第一篇文章将产生一个排行榜。

为确保您的答案显示出来,请使用以下Markdown模板以标题开头每个答案:

# Language Name, N bytes

N您提交的文件大小在哪里。如果您提高了分数,则可以将旧分数保留在标题中,方法是将它们打掉。例如:

# Ruby, <s>104</s> <s>101</s> 96 bytes

如果正确地明白,1,2,3-被放大2倍的形状,所以在2底行应作出的4个undescores,不3.
edc65

@ edc65示例中的形状大小合适。如果你正在谈论的建设的一部分,是的,这被放大并有3个下划线,这样的边数占据了第四的位置
优化

但是形状2中没有边号(在构造部分中是)。形状2的底部应等于形状1的底部
。– edc65

@ edc65哦,那里!固定!
Optimizer

3
我将标题读为“ Snow Flakes”,直到您提请注意两者之间的区别,才发现真正的标题。
mbomb007

Answers:


4

CJam,144个字节

0_]0a{{_[0X3Y0_5]f+W@#%~}%}ri*{_[YXW0WW]3/If=WI6%2>#f*.+}fI]2ew{$_0=\1f=~-
"__1a /L \2,"S/=(@\+\~.+}%_2f<_:.e>\:.e<:M.-:)~S*a*\{M.-~3$3$=\tt}/zN*

添加了换行符以避免滚动。在线尝试

该程序分几个步骤工作:

  1. 初始分形(阶数1)被编码为代表运动方向的7个角度(概念上为60°的倍数)的序列
  2. 分形被“施加”到水平段(0分形)N次,以产生所有“运动”(分形N阶)
  3. 从[0 0]开始,运动被转换为具有[xy]坐标的点序列
  4. 每个段(成对的点)转换为1或2个[xyc]三元组,表示坐标x,y处的字符c
  5. 确定边界矩形,调整坐标并生成空间矩阵
  6. 对于每个三元组,将字符c放置在矩阵中的x,y位置,并调整最终矩阵以进行输出

这个答案足够长,可以受益于字节编码:goo.gl/D1tMoc
Dennis,

@丹尼斯我不确定我已经打完高尔夫球了...为什么把它放在一块呢?
aditsu

我不太确定...您的回答令人印象深刻。我花了整整一天的时间来解决这个问题。
丹尼斯

@丹尼斯谢谢; 顺便说一句,您认为使用不可打印/控制字符通常可以吗?我通常会尽量避免他们
aditsu

如果我可以在不增加字节数的情况下避免它们,那我就做。但越短越短。:P在这种情况下,我压缩代码本身而不是压缩某些字符串或数组,通常在答案中包括两个版本。
丹尼斯

16

Python 2中,428个 411 388字节

这个很棘手。在每个步骤之后,图案都不会保留其比例,这意味着从程序上很难从其前身产生图像。该代码的作用,尽管经过一番激烈的数学练习后几乎无法理解,但实际上是使用递归定义的D函数从头到尾划清界限。

大小也是一个问题,我最终只是从一个5*3**n边角正方形的中间开始,然后裁剪了一些东西,尽管如果我想出一种更好的方法来计算大小,则可以更改它。

n=input();s=5*3**n
r=[s*[" "]for i in[0]*s]
def D(n,x,y,t=0):
 if n<1:
    x-=t%2<1;y+=t%3>1;r[y][x]='_/\\'[t/2]
    if t<2:r[y][x+2*t-1]='_'
    return[-1,2,0,1,0,1][t]+x,y-(2<t<5)
 for c in[int(i)^t%2for i in"424050035512124224003"[t/2::3]][::(t^1)-t]:x,y=D(n-1,x,y,c)
 return x,y
D(n,s/2,s/2)
S=[''.join(c).rstrip()for c in r]
for l in[c[min(c.find('\\')%s for c in S):]for c in S if c]:print l

哇,太棒了 想在AAoD#1上试一试吗?
优化器

r=[s*[" "]for i in range(s)]-> r=[[" "]*s]*s]会刮掉几个字节
sirpercival

1
@sirpercival不幸的是,不会因为工作如何*重复可变对象
grc 2015年

哦,对了,我忘了
sirpercival

您可以通过内联l,切换print'\n'.join()到for循环内的打印,使用return[...][t]+x,和从中删除括号来节省一些字节(t%2)。另外,min(c.find('\\')%s for c in S)如果您更改列表的名称,S以便不覆盖的初始值,则可以使用s
grc 2015年

12

的JavaScript(ES6),356 362 370

那是一个困难的...

每个形状都存储为一条路径。有6个基本构建基块(向后3 + 3)

  • 0斜线从左上到右下(4向后)
  • 1对角线底部从左到右(5向后)
  • 2水平从左到右(6向后)

对于每个订单,在增加订单时都需要执行一个替换步骤:

  • 0-> 0645001(向后4-> 5441024
  • 1-> 2116501(向后5-> 5412556
  • 2-> 2160224(向后6-> 0664256

h即使可以使用以下方法从0..2获得元素4..6,也可以将其预填充到数组中

;[...h[n]].reverse().map(x=>x^4).join('')

为了获得给定顺序的形状,该路径将构建在p变量中,反复应用替换。然后,主循环遍历p变量,并在g []数组(其中每个元素都是一行)内绘制形状。
从位置(0,0)开始,每个索引都可以变为负数(高阶y索引)。每当我发现负y值时,我都会避免负y索引移动所有g数组。我不在乎x索引是否变为负数,因为在JS中允许使用负数索引,只是有点难于管理。
在最后一步中,我使用.map扫描主数组,但是对于每一行,我都需要使用一个显式的for(;;)循环,该循环使用b保存达到的最小x索引(将为<0)的变量。
在里面console.log 版本中有一个方便的前导换行符,可以轻松地使尾随换行符交换2行,如摘要版本中所示。

f=o=>{
  g=[],x=y=b=0,
  h='064500192116501921602249954410249541255690664256'.split(9);
  for(p=h[2];--o;)p=p.replace(/./g,c=>h[c]);
  for(t of p)
    z='\\/_'[s=t&3],
    d=s-(s<1),
    t>3&&(x-=d,y+=s<2),
    y<0&&(y++,g=[,...g]),r=g[y]=g[y]||[],
    s?s>1?r[x]=r[x+1]=z:r[x]=z:r[x-1]=z,
    t<3&&(x+=d,y-=s<2),
    x<b?b=x:0;
  g.map(r=>
  {
    o+='\n';
    for(x=b;x<r.length;)o+=r[x++]||' '
  },o='');
  console.log(o)
}

方便的代码片段进行测试(在Firefox中):

f=o=>{
  g=[],x=y=b=0,
  h='064500192116501921602249954410249541255690664256'.split(9);
  for(p=h[2];--o;)p=p.replace(/./g,c=>h[c]);
  for(t of p)
    z='\\/_'[s=t&3],
    d=s-(s<1),
    t>3&&(x-=d,y+=s<2),
    y<0&&(y++,g=[,...g]),r=g[y]=g[y]||[],
    s?s>1?r[x]=r[x+1]=z:r[x]=z:r[x-1]=z,
    t<3&&(x+=d,y-=s<2),
    x<b?b=x:0;
  g.map(r=>
  {
    for(x=b;x<r.length;)o+=r[x++]||' ';
    o+='\n'
  },o='');
  return o
}

// TEST

fs=9;
O.style.fontSize=fs+'px'

function zoom(d) { 
  d += fs;
  if (d > 1 && d < 40)
    fs=d, O.style.fontSize=d+'px'
}
#O {
  font-size: 9px;
  line-height: 1em;
}
<input id=I value=3><button onclick='O.innerHTML=f(I.value)'>-></button>
<button onclick="zoom(2)">Zoom +</button><button onclick="zoom(-2)">Zoom -</button>
<br>
<pre id=O></pre>


6

Haskell,265个字节

(?)=div
(%)=mod
t[a,b]=[3*a+b,2*b-a]
_#[0,0]=0
0#_=3
n#p=[352,6497,2466,-1]!!((n-1)#t[(s+3)?7|s<-p])?(4^p!!0%7)%4
0&_=0
n&p=(n-1)&t p+maximum(abs<$>sum p:p)
n!b=n&[1,-b]
f n=putStr$unlines[["__ \\/   "!!(2*n#t[a?2,-b]+a%2)|a<-[b-n!2+1..b+n!2+0^n?3]]|b<-[-n!0..n!0]]

(注意:在GHC 7.10之前,您需要添加import Control.Applicative或替换abs<$>map abs$。)

在Ideone.com在线运行

f n :: Int -> IO ()绘制水平n流。图形是按位图顺序而不是沿曲线计算的,这允许算法在O(n)空间中运行(即,图形尺寸为对数)。我将近一半的字节用于计算要绘制的矩形!


我已登录,现在可以使用!真好!
Optimizer

事实证明,由于我使用的是64位Int,因此以前未在Ideone.com上运行。立即修复(牺牲2个字节)。
安德斯·卡塞格

没关系,因为登录的东西只需要我的电子邮件ID即可确认..
Optimizer

5

Perl中,334 316 309

$_=2;eval's/./(map{($_,"\1"x7^reverse)}2003140,2034225,4351440)[$&]/ge;'x($s=<>);
s/2|3/$&$&/g;$x=$y=3**$s-1;s!.!'$r{'.qw($y--,$x++ ++$y,--$x $y,$x++ $y,--$x
$y--,--$x ++$y,$x++)[$&]."}=$&+1"!eeg;y!1-6!//__\\!,s/^$x//,s/ *$/
/,print for
grep{/^ */;$x&=$&;$'}map{/^/;$x=join'',map$r{$',$_}||$",@f}@f=0..3**$s*2

在标准输入上采用的参数。测试


5

Haskell中,469个 419 390 385 365字节

函数f :: Int-> IO()以整数作为输入并打印流蛇

e 0=[0,0];e 5=[5,5];e x=[x]
f n=putStr.t$e=<<g n[0]
k=map$(53-).fromEnum
g 0=id
g n=g(n-1).(=<<)(k.(words"5402553 5440124 1334253 2031224 1345110 2003510"!!))
x=s$k"444666555666"
y=s$k"564645554545"
r l=[minimum l..maximum l]
s _[]=[];s w(x:y)=w!!(x+6):map(+w!!x)(s w y)
t w=unlines[["_/\\\\/_ "!!(last$6:[z|(c,d,z)<-zip3(x w)(y w)w,c==i&&d==j])|i<-r.x$w]|j<-r.y$w]

这将产生2倍的放大数字。我认为问题是在顶部要求较小的数字,并且仅使用2倍的放大数字来解释流蛇的构造方式。
Anders Kaseorg 2015年

你是对的。我更正了
Damien 2015年

你可以使用$的定义k,并更换(!!)a(a!!)哪些可以得到一些括号去掉。除此之外,您似乎自己也知道许多技巧。尼斯
自豪的haskeller 2015年

4

C,479 474 468 427字节

我猜没有击败Perl和Haskell的人,但是由于这里还没有C提交:

#define C char
C *q="053400121154012150223433102343124450553245";X,Y,K,L,M,N,i,c,x,y,o;F(C*p,
int l,C d){if(d){l*=7;C s[l];for(i=0;i<l;i++)s[i]=q[(p[i/7]%8)*7+i%7];return F
(s,l,d-1);}x=0;y=0;o=32;while(l--){c=*p++%8;for(i=!(c%3)+1;i--;) {K=x<K?x:K;L=
y<L?y:L;M=x>M?x:M;N=y>N?y:N;y+=c&&c<3;x-=c%5>1;if(x==X&y==Y)o="_\\/"[c%3];y-=c
>3;x+=c%5<2;}}return X<M?o:10;}main(l){F(q,7,l);for(Y=L;Y<N;Y++)for(X=K;X<=M;X
++)putchar(F(q,7,l));}

为了节省atoi()调用的空间,传递给程序的参数数量用于该级别。

该程序以O(n ^ 3)或更差的价格运行;首先,计算一次路径以找到最小/最大坐标,然后对每对(x,y)进行一次计算,以找到该特定位置上的字符。非常慢,但节省了内存管理。

示例可在http://codepad.org/ZGc648Xi上运行


使用X,Y,K,L,M,N,i,j,c;代替int X,Y,K,L,M,N,i,j,c;main(l)代替void main(int l)
Spikatrix 2015年

是的,谢谢,我已经将它们剃光了一点,我将提出一个新版本。
Zevv 2015年

最新版本的输出似乎已被修整,并且两端的输出有些偏离。
Optimizer 2015年

我上传了错误的Blob,这个应该没问题。
Zevv 2015年

4

Python 2中,523个 502 475 473 467 450 437字节

l=[0]
for _ in l*input():l=sum([map(int,'004545112323312312531204045045050445212331'[t::6])for t in l],[])
p=[]
x=y=q=w=Q=W=0
for t in l:T=t|4==5;c=t in{2,4};C=t<3;q=min(q,x);Q=max(Q,x+C);w=min(w,y);W=max(W,y);a=C*2-1;a*=2-(t%3!=0);b=(1-T&c,-1)[T&1-c];x+=(a,0)[C];y+=(0,b)[c];p+=[(x,y)];x+=(0,a)[C];y+=(b,0)[c]
s=[[' ']*(Q-q)for _ in[0]*(W-w+1)]
for t,(x,y)in zip(l,p):x-=q;s[y-w][x:x+1+(t%3<1)]='_/\_'[t%3::3]
for S in s:print''.join(S)

Pffft,花了我大约3个小时,但做起来很有趣!

这个想法是将任务分成多个步骤:

  1. 按出现顺序计算所有边缘(编码为0-5)(因此从蛇的起点到终点)
  2. 计算每个边的位置(并保存x和y的最小值和最大值)
  3. 构建它组成的字符串(并使用min值进行偏移,以免获得负索引)
  4. 打印字符串

这是非高尔夫形式的代码:

# The input
n = int(input())

# The idea:
# Use a series of types (_, /, \, %), and positions (x, y)
# Forwards:   0: __  1: /  2: \
# Backwards:  3: __  4: /  5: \

# The parts
pieces = [
    "0135002",
    "0113451",
    "4221502",
    "5332043",
    "4210443",
    "5324551"
]
# The final types list
types = [0]
for _ in range(n):
    old = types
    types = []
    for t in old:
        types.extend(map(int,pieces[t]))

# Calculate the list of positions (and store the mins and max')
pos = []
top = False
x = 0
y = 0
minX = 0
minY = 0
maxX = 0
maxY = 0
for t in types:
    # Calculate dx
    dx = 1 if t < 3 else -1
    if t%3==0:
        dx *= 2         # If it's an underscore, double the horizontal size
    # Calculate dy
    top = t in {1, 5}
    dy = 0
    if top and t in {0, 3, 1, 5}:
        dy = -1
    if not top and t in {2, 4}:
        dy = 1
    # If backwards, add dx before adding the position to the list
    if t>2:
        x += dx
    # If top to bottom, add dy before adding the position to the list
    if t in {2,4}:
        y += dy
    # Add the current position to the list
    pos += [(x, y)]
    # In the normal cases (going forward and up) modify the x and y after changing the position
    if t<3:
        x += dx
    if t not in {2, 4}:
        y += dy
    # Store the max and min vars
    minX = min(minX, x)
    maxX = max(maxX, x + (t<3)) # For forward chars, add one to the length (we never end with __'s)
    minY = min(minY, y)
    maxY = max(maxY, y)

# Create the string (a grid of charachters)
s = [[' '] * (maxX - minX) for _ in range(maxY - minY + 1)]
for k, (x, y) in enumerate(pos):
    x -= minX
    y -= minY
    t = types[k]
    char = '/'
    if t % 3 == 0:
        char = '__'
    if t % 3 == 2:
        char = '\\'
    s[y][x : x + len(char)] = char

# Print the string
for printString in s:
    print("".join(printString))

编辑:我将语言更改为python 2,以与我对#3的回答兼容(并且它还节省了6个字节)


不错的工作; 您可能要进行的一项简单改进就是更改l.extend(x)l+=x。您也可以使用codegolf.stackexchange.com/questions/54/…代替.split()您使用的(我在回答中做了类似的事情)
KSab 2015年

@KSab谢谢,我现在使用起来真的很愚蠢extend
Matty

0

帕里/ GP,395

循环遍历x,y字符位置并计算要打印的字符。尽量减少尝试,用空格评分和去除注释。

k=3;
{
  S = quadgen(-12);  \\ sqrt(-3)
  w = (1 + S)/2;     \\ sixth root of unity
  b = 2 + w;         \\ base

  \\ base b low digit position under 2*Re+4*Im mod 7 index
  P = [0, w^2, 1, w, w^4, w^3, w^5];
  \\ rotation state table
  T = 7*[0,0,1,0,0,1,2, 1,2,1,0,1,1,2, 2,2,2,0,0,1,2];
  C = ["_","_",  " ","\\",  "/"," "];

  \\ extents
  X = 2*sum(i=0,k-1, vecmax(real(b^i*P)));
  Y = 2*sum(i=0,k-1, vecmax(imag(b^i*P)));

  for(y = -Y, Y,
     for(x = -X+!!k, X+(k<3),  \\ adjusted when endpoint is X limit
        z = (x- (o = (x+y)%2) - y*S)/2;
        v = vector(k,i,
                   z = (z - P[ d = (2*real(z) + 4*imag(z)) % 7 + 1 ])/b;
                   d);
        print1( C[if(z,3,
                     r = 0;
                     forstep(i=#v,1, -1, r = T[r+v[i]];);
                     r%5 + o + 1)]) );  \\ r=0,7,14 mod 5 is 0,2,4
     print())
}

每个字符是六角形单元格的第一个或第二个。单元格位置是一个复数z,分为数为0、1,w ^ 2,...,w ^ 5的基b = 2 + w,其中w = e ^(2pi / 6)的第六个单位根。这些数字保持为1到7的区别,然后通过状态表从高到低进行净旋转。这是Ed Shoutenxytoi),但仅适用于净旋转,不能使数字沿路径成为“ N”索引。范围相对于形状中心的原点0。只要该限制不是端点,它们就位于2个字符的六边形的中间,并且仅需要这些字符中的1个。但是,当蛇的开始和/或结束为X限制时,需要2个字符,即k = 0开始且k <3结束。Pari具有内置的sqrt(-3)之类的“四元组”,但是可以分别对实部和虚部进行相同处理。


1
这不能完全满足有关前导和尾随空白的规则。
Anders Kaseorg 2015年

谢谢,我修改了。您的haskell击败了我一个小时,使它执行了x,y循环。应该在等待进一步启发之前发布:-)。
凯文·赖德

现在,将k = 0、1、2的那条蛇的末端剪掉。(数学方法很
烦人,

啊,亲爱的,当端点是x最大值时。嗯
凯文·赖德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.