Simulatore di propagazione del fuoco


28

Supponiamo di avere una matrice come questa:

11111
12221
12321
12221
11111

Questa matrice rappresenta un terreno e ogni cella rappresenta una porzione di terreno. Il numero in ogni cella rappresenta il tempo in cui la porzione di terreno deve essere completamente bruciata (in minuti, se è necessaria un'unità di misura), in base alla sua combustibilità . Se un incendio inizia in una determinata posizione (cella), quella cella deve essere completamente bruciata prima che il fuoco si propaghi alle celle adiacenti (solo orizzontale e verticale, non diagonale). Quindi, se un incendio viene avviato nella posizione centrale, il fuoco ha bisogno di:

11111        11111        11111        11011        10001        00000
12221  3 m.  12221  2 m.  12021  1 m.  11011  1 m.  00000  1 m.  00000
12321 -----> 12021 -----> 10001 -----> 00000 -----> 00000 -----> 00000
12221        12221        12021        11011        00000        00000
11111        11111        11111        11011        10001        00000

Spiegazione:

  • Il fuoco inizia da [2,2] (basato su 0), che ha un tempo di combustione di 3.
  • Dopo 3 minuti, [1,2], [2,1], [2,3], [3,2] iniziano a bruciare.
  • Dopo 2 minuti, quelle celle terminano la combustione e il fuoco si propaga a tutte le celle adiacenti, ma [0,2], [2,0], [2,4], [0,4] richiedono solo 1 minuto in più per bruciare, quindi
  • Dopo 1 minuto, queste cellule vengono bruciate e la cellula si propaga alle cellule adiacenti.
  • Dopo 1 minuto in più, il resto delle celle del passaggio 3 termina la combustione e il fuoco si propaga alle loro celle adiacenti (che sono già bruciate, quindi non succede nulla).
  • Dopo 1 ultimo minuto, il fuoco finisce bruciando l'intero terreno.

Quindi la soluzione a quel caso è di 8 minuti. Se l'incendio inizia nella cella in alto a sinistra [0,0]:

11111     01111     00111     00011     00001     00000
12221  1  12221  1  02221  1  01221  1  00121  1  00011   1
12321 --> 12321 --> 12321 --> 02321 --> 01321 --> 00321  -->
12221     12221     12221     12221     02221     01221
11111     11111     11111     11111     11111     01111

00000     00000     00000     00000     00000
00000  1  00000  1  00000  1  00000  1  00000
00221 --> 00110 --> 00000 --> 00000 --> 00000
00221     00121     00020     00010     00000
00111     00011     00001     00000     00000

Quindi ora il tempo totale è di 10 minuti.

La sfida

Data una matrice NxM (N> 0, M> 0) di valori interi che rappresentano il tempo in cui ogni cella deve essere completamente consumata, scrivere il programma / funzione più breve che prende quella matrice e una coppia di numeri interi con la posizione in cui inizia l'incendio e restituisce / stampa il tempo necessario affinché il fuoco consumi completamente l'intero terreno.

  • Ogni cella avrà un tempo di combustione positivo (diverso da zero). Non puoi assumere un valore massimo per le celle.
  • La matrice non deve essere quadrata né simmetrica.
  • La matrice può essere 0-indicizzata o 1-indicizzata, come preferisci.
  • La posizione può essere data come un singolo parametro con una tupla di numeri interi, due parametri separati di qualunque altro formato ragionevole.
  • Le dimensioni della matrice non possono essere specificate come parametri di input.
  • Non è necessario eseguire l'output di ogni passaggio intermedio, ma solo il tempo richiesto. Ma non mi lamenterò se i passaggi sono visualizzati in alcun modo.

Un altro esempio:

Fire starts at [1,1] (a '>' represents a minute):

4253   4253   4253   4153   4043   3033   2023    0001   0000
2213 > 2113 > 2013 > 1003 > 0002 > 0001 > 0000 >> 0000 > 0000 
1211   1211   1211   1111   1001   0000   0000    0000   0000

Output: 9

Questo è , quindi può vincere il programma più breve per ogni lingua!


1
@LeanderMoesinger deve funzionare con qualsiasi matrice. Quello che voglio dire è che il tuo programma o funzione non può accettare le dimensioni della matrice come parametri di input, ma ovviamente puoi calcolare quelle dimensioni all'interno del tuo codice.
Charlie,

L'input può essere preso come un singolo numero in ordine di colonna maggiore ? Cioè, le voci della matrice sono numerate, poi attraverso
Luis Mendo il

1
@LuisMendo sì, certo. Ma nota che il tempo di combustione di ogni cella può essere maggiore di 9, se questo è importante per la parte "singolo numero".
Charlie,

Grazie. No, non importa. Intendevo un singolo numero, ma forse con più cifre. Il numero varierà da 1aM*N
Luis Mendo il

Risposte:


12

Matlab, 235 257 190 182 178 byte

Input: Matrix A, vettore 1x2 pcontenente le coordinate iniziali.

function t=F(A,p)
[n,m]=size(A);x=2:n*m;x(mod(x,n)==1)=0;B=diag(x,1)+diag(n+1:n*m,n);k=sub2ind([n m],p(1),p(2));t=max(distances(digraph(bsxfun(@times,((B+B')~=0),A(:))'),k))+A(k)

Spiegazione:

Invece di simulare la propagazione del fuoco, possiamo anche capire questo come un problema "trova il percorso più lungo più breve". La matrice viene convertita in un grafico diretto ponderato. I pesi dei percorsi verso un singolo nodo corrispondono al tempo di masterizzazione di detto nodo. Ad esempio per una matrice

5   7   7   10
5   2   2   10
4   5   2   6

otteniamo il grafico collegato:

grafico

Dove nodo 1 è l'elemento della matrice in alto a sinistra e nodo 12 l'elemento in basso a destra. Date le coordinate iniziali p, viene calcolato il percorso più breve per tutti gli altri nodi. Quindi la lunghezza del percorso più lungo di quei percorsi più brevi + il tempo di masterizzare il nodo iniziale è uguale al tempo di masterizzare l'intera matrice.

Versione non modificata e commentata con valori iniziali di esempio:

% some starting point
p = [3 2];
% some random 5x6 starting map, integers between 1:10
A = randi(10,5,6); 

function t=F(A,p)
% dimensions of A
[n,m] = size(A);
% create adjacency matrix
x=2:n*m;
x(mod(x,n)==1)=0;
B = diag(x,1)+diag(n+1:n*m,n);
B = B+B';
B = bsxfun(@times,(B~=0),A(:))';
% make graph object with it
G = digraph(B);
% starting node
k = sub2ind([n m], p(1), p(2));
% calculate the shortest distance to all nodes from starting point
d = distances(G,k);
% the largest smallest distance will burn down last. Add burntime of initial point
t = max(d)+A(k);

1
Benvenuti in PPCG!
Stephen,

Approccio molto bello e molto ben spiegato!
Charlie,

Ehi, dato che questo è il mio primo golf, devo chiedere se è ok che ho omesso ;dopo ogni riga. In Matlab questi impediscono che i risultati di ciascun comando vengano visualizzati nella console. Attualmente il codice è MOLTO chiacchierone e fa spam sulla console. Ma dal momento che questo non è uno stato di fallimento rigoroso, l'ho tenuto in quel modo. Ma non importa molto, sono solo 4 byte
Leander Moesinger il

1
@LeanderMoesinger lo spam va nella stessa area di output del tuo programma? Se lo spam, ad esempio, va in STDERR o equivalente e l'output va in STDOUT o equivalente, dovresti rimuoverli. Se entrambi uscissero nello stesso punto, non lo saprei.
Stephen,

@ È un'area di output diversa, ma posso evitarlo del tutto mettendo tutto in una riga. Grazie per il chiarimento!
Leander Moesinger,

9

JavaScript (ES6), 156 152 146 144 143 byte

Salvato 1 byte grazie a Kevin Cruijssen

Un'implementazione piuttosto ingenua. Accetta input nella sintassi del curry (a)(s), dove a è un array 2D e s è un array di due numeri interi [ x, y ] che rappresentano le coordinate basate su 0 della posizione iniziale.

a=>s=>(g=t=>(a=a.map((r,y)=>r.map((c,x)=>(z=(h,v)=>(a[y+~~v]||[])[x+h]<1)(-1)|z(1)|z(0,-1)|z(0,1)|x+','+y==s&&c?u=c-1:c),u=-1),~u?g(t+1):t))(0)

Formattato e commentato

a => s => (                                // given a and s
  g = t => (                               // g = recursive function with t = time counter
    a = a.map((r, y) =>                    // for each row r of the input array:
      r.map((c, x) =>                      //   for each cell c in this row:
        (                                  //     z = function that takes
          z = (h, v) =>                    //         2 signed offsets h and v and checks
            (a[y + ~~v] || [])[x + h] < 1  //         whether the corresponding cell is 0
        )(-1) | z(1) |                     //     test left/right neighbors
        z(0, -1) | z(0, 1) |               //     test top/bottom neighbors
        x + ',' + y == s                   //     test whether c is the starting cell
        && c ?                             //     if at least one test passes and c != 0:
          u = c - 1                        //       decrement the current cell / update u
        :                                  //     else:
          c                                //       let the current cell unchanged
      ),                                   //   end of r.map()
      u = -1                               //   start with u = -1
    ),                                     // end of a.map() --> assign result to a
    ~u ?                                   // if at least one cell was updated:
      g(t + 1)                             //   increment t and do a recursive call
    :                                      // else:
      t                                    //   stop recursion and return t
  )                                        // end of g() definition
)(0)                                       // initial call to g() with t = 0

Casi test


==0può essere giocato a golf <1se non sbaglio.
Kevin Cruijssen,

1
@KevinCruijssen Questo è davvero sicuro come lo undefined<1è la falsa. Grazie!
Arnauld,

8

Ottava, 67 byte

function n=F(s,a)n=0;do++n;until~(s-=a|=imdilate(~s,~(z=-1:1)|~z')

Provalo online!

Per visualizzare risultati intermedi puoi provare questo!

Una funzione che accetta come matrice di input del terreno ae coordinate iniziali una matrice di 0 e 1 con le stesse dimensioni del terreno.

In realtà non è necessario endfunctiontuttavia eseguire l'esempio in tio che dovrebbe essere aggiunto.

Spiegazione:

Applicare ripetutamente la dilatazione morfologica dell'immagine sul terreno e sottrarre le aree bruciate da esso.

Risposta non golfata:

function n = Fire(terrain,burned)
    n = 0;
    mask = [...
            0  1  0
            1  1  1
            0  1  0];
    while true
        n = n + 1;
        propagation = imdilate(~terrain, mask);
        burned = burned | propagation;
        terrain = terrain - burned;
        if all(terrain(:) == 0)
            break;
        end
    end
end

Questa è una buona risposta, ma forse l'algoritmo sta contando lo stato iniziale come un passo e restituendo 11 invece di 10 nel tuo esempio. Se cambio la cella iniziale in [3 3] il risultato è 9 anziché 8.
Charlie

@CarlosAlejo OH, mia cattiva. Risposta aggiornata modificata n=1in n=0.
rahnema1,

7

MATL , 26 25 byte

Volevo davvero vedere altre risposte usando le lingue più golfistiche qui intorno

`yy)qw(8My~1Y6Z+fhy0>z}@&

Il formato di input è:

  • Il primo input è una matrice che utilizza ; come separatore di riga.

  • Il secondo input è un singolo numero che indirizza una voce della matrice in ordine di colonna 1 basato su maggiore (consentito dalla sfida). Ad esempio, la coordinata principale della colonna di ciascuna voce in una matrice 3 × 4 è data da

    1  4  7 10
    2  5  8 11
    3  6  9 12
    

    Ad esempio, le coordinate basate su 1 (2,2) corrispondono a 5.

Provalo online! Oppure verifica tutti i casi di test .

Spiegazione

Il codice mantiene un elenco di voci che stanno bruciando. Ad ogni iterazione, tutte le voci di tale elenco vengono ridotte. Quando una voce raggiunge lo zero, le voci adiacenti vengono aggiunte all'elenco. Per salvare i byte, le voci che raggiungono lo zero non vengono rimosse dall'elenco; invece, continuano a "bruciare" con valori negativi. Il ciclo viene chiuso quando nessuna delle voci ha valori positivi.

Guarda il programma in esecuzione passo dopo passo con un codice leggermente modificato.

Codice commentato:

`      % Do...while
  yy   %   Duplicate top two arrays (matrix and array of positions to be decremented)
       %   In the first iteration this implicitly takes the two inputs
  )    %   Reference indexing. This gives the values that need to be decremented
  q    %   Decrement
  w    %   Swap. This brings the array of positions that have been decremented to top
  (    %   Assignment indexing. This writes the decremented values back into their
       %   positions
  8M   %   Push array of positions again
  y    %   Duplicate decremented matrix
  ~    %   Negate. This replaces zeros by 1, and nonzeros by 0
  1Y6  %   Push predefined literal [0 1 0; 1 0 1; 0 1 0] (4-neighbourhood)
  Z+   %   2D convolution, maintaining size
  f    %   Find: gives column-major indices of neighbours of totally burnt entries
  h    %   Concatenate. This updates the array of positions to be decremented
  y    %   Duplicate decremented matrix
  0>   %   This gives 1 for positive entries, and 0 for the rest
  z    %   Number of nonzeros. This is the loop condition (*)
}      % Finally (execute before exiting loop)
  @    %   Push iteration number. This is the output
  &    %   Specify that the final implicit display function will display only the top
       %   of the stack
       % Implicit end. If the top of the stack (*) is not 0 (i.e. there are entries
       % that have not been totally burnt) the loop proceeds with the next iteration.
       % Else the "finally" branch is executed and the loop is exited
       % Implicit display (only top of the stack)

2
Questo è quello che chiamo un codice corto! :-)
Charlie,

4

Python 2 , 170 byte

s,m=input()
t={s}
r=0
while max(sum(m,[]))>0:
 r+=1
 for a,b in t|t:
	try:a<0<x;b<0<x;m[a][b]-=1;t|=m[a][b]==0and{(a+1,b),(a-1,b),(a,b+1),(a,b-1)}or t^t
	except:0
print r

Provalo online!


4

Python 3 , 277 266 byte

def f(m,s):
 p={s};w=len(m);t=0
 while sum(sum(m,[])):
  t+=1;i=0
  for x,y in p:
   try:m[x][y]=max(0,m[x][y]-1)
   except:0
  for v in sum(m,[]):
   if v<1:
    for l in[(1,0),(-1,0),(0,1),(0,-1)]:a,b=max(0,i%w+l[0]),max(0,i//w+l[1]);p.add((a,b))
   i+=1
 print(t)

Provalo online!

Definisce una funzione fche accetta una matrice 2D e una tupla di punti. La prima cosa la funzione fa è definire un insieme di tuple che contengono il valore iniziale tupla passato in: p={s}. La funzione passa quindi attraverso ogni tupla di punti pe sottrae uno dalla matrice min quel punto, a meno che il valore non sia già zero. Quindi passa mnuovamente in rassegna trovando tutti i punti con il valore zero e aggiungendo i quattro vicini di quel punto al set p. Questo è il motivo per cui ho scelto di usare un set, perché i set in Python non consentono valori duplicati (che rovinerebbero molto la sottrazione). Sfortunatamente, a causa dell'involucro dell'indice della lista (es. list[-1] == list[len(list)-1]:) Gli indici devono essere vincolati in modo da non diventare negativi e aggiungere le coordinate sbagliate p.

Niente di speciale, abituarsi ancora al golf. Sicuramente margini di miglioramento qui, continuerò a romperlo.


Potresti scrivere un esempio di esecuzione su Provalo online in modo che tutti possiamo testare il tuo codice?
Charlie,

@CarlosAlejo Naturalmente, l'ho aggiunto al post.
MooseOnTheRocks,

4

APL (Dyalog) , 93 66 57 byte

{⍵{^/,0≥⍺:0⋄1+x∇⍵∨{∨/,⍵∧⍲/¨2|⍳3 3}⌺3 30=x←⍺-⍵}(⊂⍺)≡¨⍳⍴⍵}

Provalo online! o visualizzalo online!


Questa funzione prende la matrice del terreno come argomento giusto e le coordinate (basate su 1) del primo fuoco come argomento sinistro. Restituisce il numero di minuti necessari per masterizzare tutto.


aggiornamenti

Finalmente ho trovato il modo di giocare a golf con la funzione spread.
* Sospiro * sarebbe molto più facile se il mondo fosse toroidale .


TIO è appena stato aggiornato a Dyalog 16.0 , il che significa che ora abbiamo il nuovo operatore stencil lucido


Risposta molto bella! L'output intermedio aiuta a visualizzare i progressi!
Charlie,

2

Python 2 , 268 byte

def f(m,y,x):t,m[y][x]=m[y][x],0;g(m,t)
def g(m,t):
	n,h,w=map(lambda r:r[:],m),len(m),len(m[0])
	for l in range(h*w):r,c=l/h,l%h;n[r][c]-=m[r][c]and not all(m[r][max(c-1,0):min(c+2,w+1)]+[m[max(r-1,0)][c],m[min(r+1,h-1)][c]])
	if sum(sum(m,[])):g(n,t+1)
	else:print t

Provalo online!

Esegui ripetutamente iterazioni temporali in cui il numero di ogni tessera viene ridotto se è cardinale adiacente a uno 0. Algoritmo molto semplice che, credo, può ancora essere giocato a golf per l'efficienza booleana ...

* nota: il mio "Provalo online!" il codice include la registrazione di debug bonus nel piè di pagina. Mi piace vedere i progressi dell'algoritmo.


2

Haskell , 138 133 byte

u#g|all((<=0).snd)g=0|2>1=1+(u:[[(x+1,y),(x-1,y),(x,y-1),(x,y+1)]|((x,y),0)<-n]>>=id)#n where n=d<$>g;d p|elem(fst p)u=pred<$>p|2>1=p

Provalo online!

Presuppone che l'input sia un elenco di ((x, y), cella). Ungolfed:

type Pos = (Int, Int)

ungolfed :: [Pos] -> [(Pos, Int)] -> Int
ungolfed burning grid
  | all ((<=0).snd) grid = 0 
  | otherwise = 1 + ungolfed (burning ++ newburning) newgrid
 where
  newgrid = map burn grid
  burn (pos,cell) | pos `elem` burning = (pos, cell - 1)
                  | otherwise = (pos, cell)
  newburning = do
    ((x,y),cell) <- newgrid
    guard (cell <= 0)
    [(x+1,y),(x-1,y),(x,y-1),(x,y+1)]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.