Побудуйте генератор льоду-головоломки + вирішувач


13

У Twitch Plays Pokémon однією з найбільш дратівливих перешкод, з якою ви можете зіткнутися, є крижана головоломка, де ви повинні подорожувати з одного місця в інше, ковзаючи по всій стороні в одну сторону, поки ви не потрапили в стіну чи валун.

Ваше завдання - створити програму, яка генерує випадкову складну крижану головоломку.

Ваша програма буде приймати три числа, M, Nі P, в якості вхідних даних (з 10 <= M <= 30, 15 <= N <= 40і 0 <= P < 65536):

12 18

і виведе:

  • MЗа Nсітці , що складається з .і O, що представляють лід і валуна відповідно.
  • Маркер позиції, який відображає, звідки введено пазл. Це положення маркера складається з однієї літери L, R, T, або B, що представляють собою зліва, справа, зверху і знизу, а потім число , яке представляє положення (зліва або зверху) на тій стороні , щоб ввести с.
  • Аналогічний маркер положення, який відображає місце, де виводиться головоломка.
  • Найкоротша рішення головоломки, що складається з послідовності L, R, Uі Dвідповідно.

Приклад виводу:

..O...O...........
............O.....
..O...............
.......O..........
..................
...........O......
O..O...........O..
..........O.......
..O..........O....
........O.........
O....O.........O..
............O.....
R 4
B 5
LDLDRULD
(Note that this output is actually invalid because it is not actually long enough.)

Для введення Mта N, рішення головоломки повинно мати принаймні min(M, N)кроки та переміщувати хоча б 2 (M + N)загальний пробіл. (Для довідки, вище головоломка рухається в цілому 12 кроків, переміщення 69 місць.) Ваша головоломка генератор повинен генерувати різний Mпо Nголоволомці з іншим шляхом вирішення (тобто інша послідовність кроків для кожного розчину) для кожного насіння P.

  • Зауважте, що вимога іншого шляху рішення полягає у тому, щоб уникнути рішень, які намагаються систематично генерувати скельні шляхи, як тут рішення Клавдіу . Якщо є дві-три пари однакових рішень через химерність у випадковості, це буде добре, якщо програма навмисно не намагається систематично генерувати пазли з однаковою послідовністю рухів.

Виграє найкоротший код для виконання вищевказаного.


2
Я не можу зрозуміти мету: "Ви повинні подорожувати з одного місця в інше, ковзаючи всю дорогу в одну сторону, поки не потрапите в стіну чи валун". Чи добре вдарити стіни чи валуни? Куди ви прагнете піти від початку? Якщо ви потрапили на валун, чи закінчується гра? Що відбувається, коли ти вдаришся об стіну? Це тільки я або напрямки незрозумілі?
DavidC

3
О, старі спогади про Покемон Золото і Срібло тут. Знайдіть вихід, сідайте на HM07 та вирушайте до міста Blackthorn.
Віктор Стафуса

3
Це нагадує мені рівень льоду в Chip's Challenge .
luser droog

1
Чому б не використовувати >та <(або будь-який символ) для входу та виходу? Загадки буде легше читати.
AL

1
Насправді ваш вибірки недійсний - найкоротший шлях, LDLDRULDякий триває лише 8 кроків
Клавдіу

Відповіді:


5

Пітон, 672 548 символів, більше цікавих пазлів

Хоча ідучи строго за правилами, моя інша програма Python перемагає цю, я вирішив написати таку, яка все-таки створить більше цікавих головоломок. Ось:

R=range;import random as J;X=J.randint
x=(0,1,-1,0);y=x[2:]+x
g=lambda r,c:(0<=r<H)+(0<=c<W)>1and f[r][c]or x[(r,c)in(A,E)]
l=lambda r,c:g(r+y[d],c+x[d])<1and(r,c)or l(r+y[d],c+x[d])
H,W,P=input();J.seed(P)
while 1:
 A=(-1,X(0,W));E=(H,X(0,W));f=[[X(0,7)for _ in R(W)]for _ in R(H)]
 q=[(A,'')];n=z={}
 while q and n!=E:
    n,O=q.pop()
    for d in R(4):
     N=l(*n)
     if g(n[0]+y[d],n[1]+x[d])and N not in z:q[:0]=[(N,O+"URLD"[d])];z[N]=1
 if(n==E)*len(O)>min(H,W):print"\n".join(''.join('O.'[c>0]for c in T)for T in f),"\nT",A[1],"\nB",E[1],"\n",O;break

Рівні відступу - це простір, вкладка, вкладка + пробіл.

Зразки :

$ echo [10,15,0] | python ice2.py
.....OO........
...............
...O....O.OO..O
...........O...
..O....O.......
.......O....O..
....O..........
.............O.
..............O
...............
T 1
B 10
DLURDRURULDRD

Він використовується Pяк насіння, тож кожен Pгенерує одну і ту ж головоломку, і кожен з Pних дуже імовірно відрізняється:

$ echo [10,15,1] | python ice2.py
.OOO.O.........
...O......O.O.O
.......O.......
..O..........OO
.....O.........
.............O.
.O.............
.O............O
O....O.........
......O........
T 14
B 8
DLDRDLURULD

Він працює досить швидко до розмірів, M=25,N=40але минулий, що стає дуже повільним. Теоретично це повинно працювати, M=30, N=40якщо ви даєте йому працювати досить довго. Я вручну написав сюди слід, оскільки важко слідувати - програма просто виводить пазл.

$ echo [25,40,0] | python ice2.py
                   *
...................dO....urrrO..O..O....
....O.....O........dO....u..dO..........
..........O.....O..d....Ou.Odrrrrrrrrrrr
...........O.......d.O..Ou..O.....OOllld
.O....O.OO.........drrrrrrO....Olllud..O
O......O...O.O.....O............dO.ud...
O........OO..........O.........Od..ud..O
.........O......................d..ud...
....O.....O.O....O.....O........d..ud.O.
.....O..O...................O...d..udO..
.........O.........O..O.........d..ud...
.......O.O...O..O.OO....O...OOlldOOld...
........Olllllllllu....OO.OO..dOO...O...
.O.O....Od........u......O....d..O...O..
..O....O.d........u..O........d..O..O...
....O....d..O.....uO.....O....d.........
.........d........u...........d.........
.........d....O...u.O..O.....Od.O.......
........Od...O....u...........d.........
.O.....OuxrrrrO...u...OOOO..O.d.........
........udO..dO.O.u...........d.........
O..O.O..ud...d..urrO..........d.O...O...
........ud...d..u.O.O........Od..O...O..
..OO....ud..Od..u......OllllludO.....O..
..O....OldO..dOOlllllllld...Old...O..O..
             *
T 19
B 13
DRURDRDLDLULDLDLULDLURULDLURD

Пояснення :

Програма циклічно, генеруючи випадкове стартове положення у верхній частині, випадкове кінцеве положення на дні та випадкову сітку з 12.5%шансом на валун на будь-якому місці. Потім він вирішує головоломку за шириною першого пошуку, і якщо рішення існує і більше, ніж min(H,W)воно друкується та виходить.


4

Ява - 2632

Хоча я захоплююсь технічною чистотою відповіді Клавдіу , я вирішив спробувати свої сили, щоб скласти трохи складніші пазли;)

Основні кроки (досить прості):

Randomize entry location
Step forward
For min(m,n)-1 steps:
    Rotate left or right
    Slide until I hit something or go a random distance
    Place a rock in front of stopping location
If I can slide straight to any wall:
    Slide to exit
Else
    Create another step and try again

If at any step I get trapped, start over
If BFS finds shorter path, start over

Я також відзначаю кожну пляму як "nogo", коли я ковзаю. Якщо я опиняюсь на ного-місці (або прямо перед тим, що означало б, що туди йде скеля), це недійсний крок.

Отже, в основному ідея полягає в тому, щоб випадковим чином генерувати безліч карт і зберігати першу, яка є дійсною. Я планую зробити це розумнішим (зворотний трек тощо), але він працює нормально зараз. Це також може скоротити якийсь надмірний код, ми побачимо.

Наразі, він створює малі карти (15x10) майже миттєво, середні (30x20) карти за пару секунд, а великі (40x30) за деякий випадковий проміжок часу від 20 секунд до 20 хвилин, залежно від насіння. Він перевіряє на моїй машині від 300 до 500 тис. Карт / секунду, залежно від розміру.

Бічна примітка: Іноді карти не надто важкі, просто тому, що є лише стільки скель, як сходинок, і якщо тільки крок не приведе вас до стіни, то в більшості випадків є лише один варіант, якщо ви хочете вдарити справжню скелю. Я це вирішу пізніше, розміщуючи "випадкові" скелі в безпечних місцях після того, як всі кроки будуть проведені. Оскільки плями nogo вже позначені, це повинно бути досить простим. Наразі просто насолоджуйтесь такими прикладами:

Вихід із зазначенням різних розмірів / насіння:

$ java I 30 20 6851              $ java I 15 10 1     $ java I 15 10 65513  

............................O.      .......O.......     ....O..........     
..............................      ...............     ...............     
..............................      .........O.....     .........O.....     
..........O......O............      .............O.     ..............O     
...............O...........O..      ...............     ...............     
..............................      .......O.......     .....O.O.......     
..............................      O..............     ...............     
........................O.....      ...............     ..........O....     
..............................      ...............     O..............     
...O.......................O..      ......O........     ...............     
O...............O.OO..........          
..............O..........O....          
...........O..................      T 14                R 6         
....O.........................      T 7                 T 14            
..............................      DLDLULURU           LULDLDRURU
..............................
..............................
.................O............
.O............................
..............................


B 28
R 9
ULURDLDLDRURDLDRURUR

Максимальний розмір 40x30:

$ java I 40 30 2

........................................
........................................
........................................
........................................
................O.......................
..........O.............................
........................................
.......O................................
.....................O..........O.......
......................O.................
.................................O......
......................................O.
........................................
........................................
..............................O.........
...........O............................
........................................
.......................................O
.........O...................O..........
....................O...................
...............................O........
............O..O......................O.
......O...........O.....................
..................O....O................
..................................O.....
........................................
..............................O.........
.....................................O..
...........O............................
...................O....................

B 19
B 11
URURDLULULDRDRDLULDLDLULURDLD

Гольф:

import java.util.*;import java.awt.*;class I{int m,n,p,g,a[][],b[][];Random r;Point s,e,c;ArrayList<Integer>z;void Q(String q,int l){if(l>0)System.out.println(q);else System.out.print(q);}void G(String[]y){m=Integer.valueOf(y[0]);n=Integer.valueOf(y[1]);p=Integer.valueOf(y[2]);r=new Random(p);Q("",1);int o=0,i,j,u=0;char t,f[]={85,76,68,82};while(o<3){if(++u%20000==0)Q("\r#"+u,0);a=new int[m+2][n+2];b=new int[m+2][n+2];for(i=0;i<m+2;i++)for(j=0;j<n+2;j++)if(i==0||i==m+1||j==0||j==n+1)a[i][j]=2;s=new Point();int e=r.nextInt(m*2+n*2);if(e<m*2){s.x=e%m+1;s.y=e<m?0:n+1;}else{s.y=(e-m*2)%n+1;s.x=(e-m*2)<n?0:m+1;}if(s.x<1)g=3;else if(s.x>m)g=1;else if(s.y<1)g=2;else if(s.y>n)g=0;a[s.x][s.y]=0;c=new Point(s);z=new ArrayList<Integer>();z.add(g);for(i=0;i++<Math.min(m,n)-1;)if(N()<1&&N()<1)break;o=((z.size()>=Math.min(m,n)-1)?1:0)+F()+((V()==z.size())?1:0);}Q("\r",0);for(j=1;j<n+1;j++){for(i=1;i<m+1;i++)Q(String.valueOf(a[i][j]>0?'O':'.'),0);Q("",1);}Q("\n\n",0);if(s.x<1||s.x>m){t=s.x<1?'L':'R';u=s.y;}else{t=s.y<1?'T':'B';u=s.x;}Q(t+" "+u,1);if(e.x<1||e.x>m){t=e.x<1?'L':'R';u=e.y;}else{t=e.y<1?'T':'B';u=e.x;}Q(t+" "+u,1);for(i=0;i<z.size();)Q(String.valueOf(f[z.get(i++)]),0);Q("",1);}public static void main(String[]a){new I().G(a);}int F(){int c=0;while(C()<1&&c++<10)if(N()<1)return 0;return e==null?0:1;}int C(){int d=g<2?-1:1;if(g%2<1){int y=c.y;while(y>0&&y<n+1){y+=d;if(a[c.x][y]==1)return 0;}e=new Point(c.x,y);}else{int x=c.x;while(x>0&&x<m+1){x+=d;if(a[x][c.y]==1)return 0;}e=new Point(x,c.y);}a[e.x][e.y]=0;return 1;}int V(){if((s.x-e.x)+(s.y-e.y)<2)return 0;Queue<Point>q=new ArrayDeque<Point>();Queue<Integer>d=new ArrayDeque<Integer>();a[s.x][s.y]=-2;q.add(s);d.add(0);while(q.size()>0){Point t=q.poll();int h=d.poll(),i=0;if(t.equals(e))return h;for(;i<4;i++){Point n=S(a,t,i<2?0:1,i%2<1?-1:1,99,1);if(a[n.x][n.y]==-2)continue;a[n.x][n.y]=-2;q.add(n);d.add(h+1);}}return 0;}int N(){Point q;int d=g<2?-1:1,x,y;System.arraycopy(a,0,b,0,a.length);q=S(b,c,g,d,r.nextInt((g%2<1?n:m)/2)+2,0);if(q.x<1||q.y<1||q.x>m||q.y>n||q.equals(c)||b[q.x][q.y]!=0)return 0;x=q.x;y=q.y;if(g%2<1)y+=d;else x+=d;if(b[x][y]<0)return 0;b[q.x][q.y]=-1;b[x][y]=1;int f=r.nextInt(2)<1?-1:1;g=g%2<1?(f<0?1:3):(g=f<0?0:2);c=q;System.arraycopy(b,0,a,0,a.length);z.add(g);return 1;}Point S(int[][]u,Point f,int w,int d,int q,int s){int i=1,x=f.x,y=f.y;for(;i<=q;i++){if(w%2<1)y=f.y+i*d;else x=f.x+i*d;if(e!=null&&e.x==x&&e.y==y)return e;if(y<0||y>n+1||x<0||x>m+1)return f;if(s<1&&u[x][y]<1)u[x][y]=-1;if(u[x][y]>0){if(w%2<1)y-=d;else x-=d;return new Point(x,y);}}if(w%2<1)return new Point(f.x,f.y+i*d);else return new Point(f.x+i*d,f.y);}}

З розривами рядків:

import java.util.*;
import java.awt.*;

class I{
    int m,n,p,g,a[][],b[][];
    Random r;
    Point s,e,c;
    ArrayList<Integer>z;

    void Q(String q,int l){if(l>0)System.out.println(q);else System.out.print(q);}

    void G(String[]y){
        m=Integer.valueOf(y[0]);
        n=Integer.valueOf(y[1]);
        p=Integer.valueOf(y[2]);
        r=new Random(p);
        Q("",1);

        int o=0,i,j,u=0;
        char t,f[]={85,76,68,82};
        while(o<3){
            if(++u%20000==0)
                Q("\r#"+u,0);

            a=new int[m+2][n+2];
            b=new int[m+2][n+2];
            for(i=0;i<m+2;i++)
                for(j=0;j<n+2;j++)
                    if(i==0||i==m+1||j==0||j==n+1)
                        a[i][j]=2;

            s=new Point(); 
            int e=r.nextInt(m*2+n*2);
            if(e<m*2){
                s.x=e%m+1;
                s.y=e<m?0:n+1;
            }else{
                s.y=(e-m*2)%n+1;
                s.x=(e-m*2)<n?0:m+1;
            }
            if(s.x<1)g=3;
            else if(s.x>m)g=1;
            else if(s.y<1)g=2;
            else if(s.y>n)g=0;

            a[s.x][s.y]=0;
            c=new Point(s);
            z=new ArrayList<Integer>();
            z.add(g);

            for(i=0;i++<Math.min(m,n)-1;)
                if(N()<1&&N()<1)
                        break;
            o=((z.size()>=Math.min(m,n)-1)?1:0)+F()+((V()==z.size())?1:0);
        }

        Q("\r",0);
        for(j=1;j<n+1;j++){
            for(i=1;i<m+1;i++)
                Q(String.valueOf(a[i][j]>0?'O':'.'),0);
            Q("",1);
        }
        Q("\n\n",0);
        if(s.x<1||s.x>m){
            t=s.x<1?'L':'R';
            u=s.y;
        }else{
            t=s.y<1?'T':'B';
            u=s.x;
        }
        Q(t+" "+u,1);
        if(e.x<1||e.x>m){
            t=e.x<1?'L':'R';
            u=e.y;
        } else {
            t=e.y<1?'T':'B';
            u=e.x;
        }
        Q(t+" "+u,1);
        for(i=0;i<z.size();)
            Q(String.valueOf(f[z.get(i++)]),0);
        Q("",1);
    }

    public static void main(String[]a){
        new I().G(a);
    }

    int F(){
        int c=0;
        while(C()<1&&c++<10)
            if(N()<1)
                return 0;
        return e==null?0:1;
    }

    int C(){
        int d=g<2?-1:1;
        if(g%2<1){
            int y=c.y;
            while(y>0&&y<n+1){
                y+=d;
                if(a[c.x][y]==1)
                    return 0;
            }
            e=new Point(c.x,y);
        }else{
            int x=c.x;
            while(x>0&&x<m+1){
                x+=d;
                if(a[x][c.y]==1)
                    return 0;
            }
            e=new Point(x,c.y);
        }
        a[e.x][e.y]=0;
        return 1;
    }


    int V(){
        if((s.x-e.x)+(s.y-e.y)<2)
            return 0;
        Queue<Point>q=new ArrayDeque<Point>();
        Queue<Integer>d=new ArrayDeque<Integer>();
        a[s.x][s.y]=-2;

        q.add(s);
        d.add(0);
        while(q.size()>0){
            Point t=q.poll();
            int h=d.poll(),i=0;
            if(t.equals(e))
                return h;
            for(;i<4;i++){
                Point n=S(a,t,i<2?0:1,i%2<1?-1:1,99,1);
                if(a[n.x][n.y]==-2)
                    continue;
                a[n.x][n.y]=-2;
                q.add(n);d.add(h+1);
            }
        }
        return 0;
    }


    int N(){
        Point q;
        int d=g<2?-1:1,x,y;
        System.arraycopy(a,0,b,0,a.length);
        q=S(b,c,g,d,r.nextInt((g%2<1?n:m)/2)+2,0);      
        if(q.x<1||q.y<1||q.x>m||q.y>n||q.equals(c)||b[q.x][q.y]!=0)
            return 0;
        x=q.x;
        y=q.y;
        if(g%2<1)
            y+=d;
        else
            x+=d;
        if(b[x][y]<0)
            return 0;
        b[q.x][q.y]=-1;
        b[x][y]=1;
        int f=r.nextInt(2)<1?-1:1;          
        g=g%2<1?(f<0?1:3):(g=f<0?0:2);
        c=q;
        System.arraycopy(b,0,a,0,a.length);
        z.add(g);
        return 1;
    }

    Point S(int[][]u,Point f,int w,int d,int q,int s){
        int i=1,x=f.x,y=f.y;
        for(;i<=q;i++){
            if(w%2<1)
                y=f.y+i*d;
            else
                x=f.x+i*d;
            if(e!=null&&e.x==x&&e.y==y)
                return e;
            if(y<0||y>n+1||x<0||x>m+1)
                return f;
            if(s<1&&u[x][y]<1)
                u[x][y]=-1;
            if(u[x][y]>0){
                if(w%2<1)
                    y-=d;
                else
                    x-=d;
                return new Point(x,y);
            }
        }
        if(w%2<1)
            return new Point(f.x,f.y+i*d);
        else
            return new Point(f.x+i*d,f.y);              
    }
}

Не могло while(o<3){...;o=...;}бути for(;o<3;o=...){...;}, зберегти один байт?
Джонатан Фрех

if(w%2<1)return new Point(f.x,f.y+i*d);else return new Point(f.x+i*d,f.y);-> return new Point(f.x+(w%2<1?0:i*d),f.y+(w%2<1?f.y:0));.
Джонатан Фрех

3

Пітон, 235 206 185 176 символів

H,W,P=input()
t=''
for x in range(16):t+=".O"[(P>>x)%2]
for n in[t[1:],t[0],"O","...O"]+["."]*(H-5)+[".O.."]:print(n*W)[:W]
print"B 1\nR",(H,3)[-W%4/2],"\n",("URDR"*W)[:W+W%2]

Використання :

Введення здійснюється через stdin форми [M, N, P].

$ echo [14, 17, 2] | python ice.py
O..............O.
.................
OOOOOOOOOOOOOOOOO
...O...O...O...O.
.................
.................
.................
.................
.................
.................
.................
.................
.................
.O...O...O...O...
B 1
R 3
URDRURDRURDRURDRUR

Ви сказали, що карти повинні бути різними для кожного насіння P... і це:

$ echo [14, 17, 233] | python ice.py
..O.OOO..........
OOOOOOOOOOOOOOOOO
OOOOOOOOOOOOOOOOO
...O...O...O...O.
.................
.................
.................
.................
.................
.................
.................
.................
.................
.O...O...O...O...
B 1
R 3
URDRURDRURDRURDRUR
$ echo [14, 17, 65133] | python ice.py
.OO.OO..OOOOOOO.O
OOOOOOOOOOOOOOOOO
OOOOOOOOOOOOOOOOO
...O...O...O...O.
.................
.................
.................
.................
.................
.................
.................
.................
.................
.O...O...O...O...
B 1
R 3
URDRURDRURDRURDRUR

І приклад без різного розміру:

$ echo [10, 15, 65133] | python ice.py
.OO.OO..OOOOOOO
OOOOOOOOOOOOOOO
OOOOOOOOOOOOOOO
...O...O...O...
...............
...............
...............
...............
...............
.O...O...O...O.
B 1
R 10
URDRURDRURDRURDR

Задовольняє всі поставлені об'єктивні критерії:

  • Кожен Pведе до різної загадки
  • Є лише одне рішення, таким чином воно є найкоротшим
  • Рішення вживає N + N%2кроків, що принаймніN
  • Рішення завжди займає більше 2 (M + N)загального простору

Пояснення :

Кожен рядок будується шляхом повторення певного часу рядкового елемента Wта обмеження довжини W(я використовую Hі Wзамість Mі N).

Перші два ряди залежать від того, Pщоб зробити кожен пазл унікальним. В основному зауважте, що він Pвписується в 16-бітове ціле число, яке не має значення. Я перетворюю Pна двійкові, використовуючи .для 0 і Oдля 1:

t=''
for x in range(16):t+=".O"[(P>>x)%2]

Перший елемент рядка є останнім 15 біт, t[1:], в той час як другий ряд елементів є 1 - й біт, t[0]. Я не зміг би поставити все це в один ряд, оскільки мінімальна ширина - 15, що не відповідатиме всім 16 бітам, якщо P> 32767. Таким чином, перші два ряди однозначно представляють кожне з можливих значень P.

Третій ряд - це повна стіна, щоб значення Pне впливало на рішення.

Потім виконайте фактичні елементи лабіринту. Цей рядок роздруковує їх усіх, повторюючи їх до верхньої частини. Результат, як ви бачите вище:

for n in[t[1:],t[0],"O","O..."]+["."]*(H-5)+["..O."]:print(n*W)[:W]

Решта лише з'ясовувала, як вирішити динамічно генерований лабіринт. Це залежить лише від ширини лабіринту. Я зазначив, що рішення для заданої ширини були:

  W  | solution 
-----+---------
  1  | UR
  2  | UR
  3  | UR DR
  4  | UR DR 
  5  | UR DR UR
  6  | UR DR UR
  7  | UR DR UR DR
  8  | UR DR UR DR

і т.д. Таким чином , це просто URDRповторюється , і відрізати в потрібному місці, W+W%2.

print"B 1\nR",(H,3,3,H)[W%4],"\n",("URDR"*W)[:W+W%2]

1
як до 33-го біта цілого числа це працює?
masterX244

@ masterX244: Багато і багато гольфу ... в основному використовуючи повторюваний характер результатів і роблячи певну математику, щоб переконатися, що все правильно складається
Клавдіу,

здебільшого цікаво про те, як робиться "випадковість" (PS голосування не було від мене)
masterX244

@ masterX244: ах готча. Додам пояснення
Клавдіу

1
Я не мав на увазі це негативно. Це напевно розумно, я просто сподіваюсь, що працьовані ігрові розробники не використовуватимуть це для фактичних головоломок: p
Geobits
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.