Створіть непересічний шлях ascii-art


18

Дано 2 цілих входи, що представляють розмір поля, xі yвиведіть шлях через поле.

Приклад виводу для 5, 4:

#    
#    
# ###
### #

Все поле 5 на 4, і є шлях, зроблений з хештеків, що перетинають поле.

Шлях завжди повинен починатися у верхньому лівому куті та йти в правому нижньому куті. Весь шлях повинен бути рандомізований кожного разу при запуску програми. Кожен дійсний шлях повинен бути можливим результатом.

Правилами шляхів є:

  • Зроблено з хеш-знаків

  • Кожен хеш з'єднаний лише з двома іншими хешами (тобто шлях не перетинається і не проходить поряд із собою)

Не хеш-пробіли можуть заповнювати його будь-яким іншим символом, але він повинен бути узгодженим (тобто всі пробіли, усі періоди тощо)

Приклади:

2, 2

##
 #

3, 4

##
 ##
  #
  #

5, 5

#####
    #
    #
    #
    #

6, 5

## ###
 # # #
## # #
# ## #
###  #

7, 9

#######
      #
####  #
#  #  #
#  #  #
#  #  #
#  ####
#
#######

Такий шлях схожий на випадковий прогулянку, що уникає самовираження, проте він не може бути суміжним із собою, на відміну від справжнього ПАВ.

Безперервність та дотик до контуру визначаються без діагоналей.


Вихідний формат RBX.Lua? ;)
devRicher

Чи правильно, що якщо кожен дійсний шлях має позитивну ймовірність обрання, розподіл ймовірностей є довільним?
недолік

@devRicher так
Rɪᴋᴇʀ

@flawr Так, це правильно
Rɪᴋᴇʀ

Відповіді:


11

MATLAB, 316 305 300 293 байт

function P=f(a,b);z(a,b)=0;P=z;c=@(X)conv2(+X,[0,1,0;1,1,1;0,1,0],'s');B=1;while B;P=z;P(1)=1;for K=1:a*b;S=z;S(a,b)=1;for L=2:a*b;S(~S&c(S)&~P)=L;end;[i,j]=find(S&c(P==K));if i;r=randi(nnz(i));else;break;end;P(i(r),j(r))=K+1;if P(a,b);break;end;end;B=any(any(c(P>0)>3));end;P(P>0)=35;P=[P,'']

Дякую @LuisMendo за різні пропозиції та купу байтів =)

Спробуйте в Інтернеті! (Без гарантії. Зверніть увагу, що для його запуску на Octave потрібно було декілька коригувань. По-перше, мені потрібно було видалити functionключове слово та жорсткий код значень, по-друге: пробіли не друкуються правильно, як у Matlab. Також я не перевірити команди згортання Octave, які можуть діяти по-різному.)

Приклад виводу для введення (7,10)(може вже зайняти досить багато часу):

#         
#         
##        
 ##       
  #   ### 
  #   # ##
  #####  #

Пояснення

Це генерує контури послідовно зверху вліво праворуч знизу з потрібним 4-з'єднанням, а потім використовує вибірку відхилення для відхилення контурів, які порушують критерій, що у вас не може бути суміжних частин.

function P=f(a,b);
z(a,b)=0;                                 % a matrix of zeros of the size of th efield
P=z;                                    
c=@(X)conv2(+X,[0,1,0;1,1,1;0,1,0],'s');  % our convolution function, we always convolute with the same 4-neighbourhood kernel
B=1;
while B;                                  % while we reject, we generate paths:
    P=z;
    P(1)=1;                               % P is our path, we place the first seed
    for K=1:a*b;                          % in this loop we generate the all shortest paths (flood fill) from the bottom right, withot crossing the path to see what fiels are reachable from the bottom left
        S=z;
        S(a,b)=1;                         % seed on the bottom left
        for L=2:a*b;
            S(~S&c(S)&~P)=L;              % update flood front
        end;
        [i,j]=find(S&c(P==K));            % find a neighbour of the current end of the path, that is also reachable from the bottom left
        if i;                             % if we found some choose a random one
            r=randi(nnz(i));
        else;
            break;                        % otherwise restart (might not be necessary, but I'm too tired to think about it properly=)
        end;
        P(i(r),j(r))=K+1;                 % update the end of the current path
        if P(a,b);                        % if we finished, stop continuing this path
            break;
        end;
    end;
    B=any(any(c(P>0)>3));                 % check if we actually have a valid path
end;
P(P>0)=35;                                % format the path nicely
P=[P,''];

Ой і як завжди:

Конволюція - запорука успіху.


19

Befunge, 344 байт

&v>>>#p_:63p:43g`\!+v>/*53g+\01g:2%2*1-\2/!*63g+\0\:v
 40$ v++!\`g14:p35:\<^2\-1*2%2p10::%4+g00:\g36\g35-1_v
#11^$_83p73v >1+:41g`!#v_$,1+:43g`!#v_@>->2>+00p+141^_
<p1^     vp< ^,g+7g36:<<<<1+55p36:<<<< ^1?0^#7g36g35*
8&p|!++!%9#2g+7g10\*!-g38g10!-g37:g00!!*<>3^
443>:!#v_>>1-::3%1-:53g+00p\3/1-:63g+01p^
^>^>>$#<"#"53g63g7+p41g53g-43g63g-+!#^_

Спробуйте в Інтернеті!

Як зазначає @flawr у своїй відповіді MATLAB, це може зайняти деякий час, якщо розмір поля має будь-який нетривіальний розмір. Насправді досить легко потрапити в ситуацію, коли насправді не варто намагатися чекати, коли це закінчиться, адже ти, швидше за все, чекаєш кінця часу.

Щоб зрозуміти, чому це відбувається, корисно переглянути програму, як вона виконується в одному з багатьох "візуальних налагоджувачів" Befunge. Оскільки дані та код - це одне і те ж саме в Befunge, ви зможете переглянути шлях, який змінюється з часом. Наприклад, ось коротка анімація, яка показує, як може виглядати частина пробігу по повільному шляху.

Анімація, що показує, як будівництво шляху застрягло в кутку

Після того, як алгоритм вирішив зробити цей доленосний поворот ліворуч у нижній частині поля поля, він по суті засудив себе на життя безцільного блукання. З цього моменту він повинен пройти кожен можливий шлях у відгородженій ділянці, перш ніж він зможе відступити назад і спробувати повернути праворуч. А кількість потенційних шляхів у цих випадках може легко стати астрономічною.

Підсумок: Якщо, здається, це зайняло тривалий час, можливо, це гарна ідея просто перервати виконання та почати заново.

Пояснення

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

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

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

Ось як виглядає код із виділеними різними компонентами:

Вихідний код із виділеними шляхами виконання

*Прочитайте ширину та висоту поля та натисніть на початкові координати разом із 0маркером типу, щоб вказати потенційний шлях, а не місце відстеження.
*Перевірте наявність місць зворотного відстеження (позначених 1маркером типу), які повертаються за допомогою простої pкоманди, оскільки вони зберігаються у точному форматі, необхідному для запису місця на поле для відтворення.
*Перевірте, чи все ще є координати всередині ігрового поля. Якщо вони перебувають поза діапазоном, скиньте їх із стека та петлі назад, щоб спробувати наступні потенційні координати. Перевірте, чи будуть координати контактувати з існуючим відрізком шляху. Місце попереднього кроку, очевидно, ігнорується з цієї перевірки. Якщо всі тести успішні, напишіть а
*Якщо вони знаходяться в діапазоні, отримайте наступні два значення зі стека, що є місцем попереднього кроку (необхідного в тесті, який слід за цим).
*
*#на ігрове поле і перевірте, чи ми дійшли до місця призначення.
*Якщо у нас є, випишіть остаточний шлях *та вийдіть.
*В іншому випадку збережіть координати на стеці 1маркером типу для подальшого зворотного відстеження.
*Це переривається випадковим підрахунком чисел, який нам знадобиться незабаром. Поверніться до початку основного циклу і обробіть наступні значення на стеку.
*Натисніть чотири потенційні пункти, до яких можна дістатися з поточного місця розташування. Випадкове число визначає порядок, в якому вони висуваються, і, таким чином, порядок їх дотримання.
*


2
Свята корова. Пояснення?
Rɪᴋᴇʀ

@EasterlyIrk Дякую за виграш. Це дуже цінується.
Джеймс Холдернесс

Радий, що це було корисно!
Rɪᴋᴇʀ

2

QBasic, 259 байт

Я впевнений, кохання GOTOs.

RANDOMIZE TIMER
INPUT w,h
DO
CLS
x=1
y=1
REDIM a(w+3,h+3)
2a(x+1,y+1)=1
LOCATE y,x
?"#"
d=INT(RND*4)
m=1AND d
x=x+m*(d-2)
y=y-d*m+m+d-1
c=a(x,y+1)+a(x+2,y+1)+a(x+1,y)+a(x+1,y+2)=1
IF(x=w)*c*(y=h)GOTO 9
IF(x*y-1)*x*y*c*(x<=w)*(y<=h)GOTO 2
LOOP
9LOCATE y,x
?"#"

Основна стратегія: на кожному кроці надрукуйте a #до поточного місця та рухайтесь у випадковому напрямку. Масив a0 і 1 відстежує, де ми були. Якщо переміщення легальне і перенесе нас до кінцевої точки, GOTO 9вийти з циклу та надрукувати фінал #. В іншому випадку, якщо цей крок законний, зробіть ще один крок. В іншому випадку очистіть екран і почніть спочатку (набагато гольфіст, ніж кодування алгоритму зворотного відстеження!).

Випробуваний на моєму ноутбуці в QB64, це, як правило, дає результат протягом 9, 9п'яти секунд або менше. Виконання 10, 10часу пройшло десь від трьох до 45 секунд. Теоретично всі юридичні шляхи мають ненульову ймовірність, але ймовірність шляху з великими кривими суттєво мала. Я час від часу бачив стежки з однією або двома маленькими кривими, хоча:

Зразок шляху

Необоротна версія та / або глибоке пояснення, доступне на запит.


2

R, 225 байт

function(x,y){library(igraph);a=matrix(rep(" ",x*y),c(y,x));g=make_lattice(c(y,x));w=runif(ecount(g));for (i in 1:2){v=shortest_paths(g,1,x*y,weights=w)$vpath[[1]];w[]=1;w[E(g)[v%--%v]]=0;};a[v]="#";cat(rbind(a,"\n"),sep="")}

Пояснення:

Ми генеруємо правильний (гратчастий) [x * y] непрямий графік із випадковими вагами на ребрах, тоді ми знаходимо найкоротший шлях від початку до кінця. Однак у генерованому шляху можуть бути клітини, у яких є більше двох сусід, наприклад:

#
#
####
  ##
  #
  ###

Тож ми повинні два рази застосувати алгоритм найкоротшого шляху. Вдруге ми встановимо всі ваги до 1, за винятком тих, що знаходяться в поточному знайденому шляху, який встановлений на 0;

результат

#
#
### 
  # 
  #
  ###

Безумовно:

function(x,y){
    library(igraph);#igraph library should be installed
    a=matrix(rep(" ",x*y),c(y,x));#ASCII representation of the graph
    g=make_lattice(c(y,x));# regular graph
    w=runif(ecount(g));#weights
    for (i in 1:2){
        #find vertices that are in the path
        v=shortest_paths(g,1,x*y,weights=w)$vpath[[1]];
        #set all weights to 1 except those that are in the current found path that set to 0
        w[]=1;
        w[E(g)[v%--%v]]=0;
    }
    a[v]="#";
    cat(rbind(a,"\n"),sep="")
}

1

JavaScript (ES7), 333 331 330 329 324 318 312 байт

f=
(h,w,c=[...Array(h)].map(_=>Array(w).fill` `),g=a=>{for(z of b=[[[h-1,w-1]]])a.map(([x,y])=>b.every(([[i,j]])=>i-x|j-y)&(z[0][0]-x)**2+(z[0][1]-y)**2<2&&b.push([[x,y],...z]));return b.find(([[x,y]])=>!x&!y)||g([...a,[h,w].map(n=>Math.random()*n|0)])})=>g([]).map(([x,y])=>c[x][y]=`#`)&&c.map(a=>a.join``).join`
`
Height: <input type=number min=1 id=h>Width: <input type=number min=1 id=w><input type=button value="Generate!" onclick=o.textContent=f(+h.value,+w.value)><pre id=o>

Пояснення: #s випадковим чином розміщуються в масиві, поки шлях не буде знайдений через поле за допомогою першого пошуку в широті; перший, і тому найкоротший, такий шлях потім виводиться; це гарантує, що шлях не перетинається сам по собі. Зауважте, що особливо для великих полів можливо перевищити стек двигуна JS перед тим, як знайти шлях. Безголівки:

function r(n) {
    return Math.floor(Math.random() * n);
}
function f(h, w) {
    var a = []; // array of placed #s
    var b; // breadth-first search results
    var c;
    do {
        a.push([r(h), r(w)]); // place a # randomly
        b = [[[h - 1, w - 1]]]; // start from bottom right corner
        for (z of b) // breadth-first search
            for ([x, y] of a) // find possible next steps
                if (!b.some(([[i, j]]) => i == x && j == y))
                    if ((z[0][0] - x) ** 2 + (z[0][1] - y) ** 2 < 2)
                        if (x || y)
                            b.push([[x, y], ...z]); // add to search
                        else if (!c)
                            c = [[x, y], ...z]; // found path
    } while (!c);
    a = [...Array(h)].map(() => Array(w).fill(' '));
    for ([x, y] of c) // convert path to output
        a[x][y] = '#';
    return a.map(b => b.join('')).join('\n');
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.