Feuerausbreitungssimulator


28

Angenommen, wir haben eine Matrix wie diese:

11111
12221
12321
12221
11111

Diese Matrix repräsentiert ein Gelände und jede Zelle repräsentiert einen Teil des Geländes. Die Zahl in jeder Zelle gibt die Zeit an, die der Teil des Geländes entsprechend seiner Brennbarkeit vollständig verbrannt werden muss (in Minuten, wenn eine Maßeinheit benötigt wird) . Beginnt ein Feuer an einer bestimmten Position (Zelle), muss diese Zelle vollständig verbrannt werden, bevor sich das Feuer auf die benachbarten Zellen ausbreitet (nur horizontal und vertikal, nicht diagonal). Wenn also ein Feuer in der Mittelposition ausgelöst wird, benötigt das Feuer:

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

Erläuterung:

  • Feuer beginnt bei [2,2] (0-basiert), was eine Brenndauer von 3 hat.
  • Nach 3 Minuten beginnen [1,2], [2,1], [2,3], [3,2] zu brennen.
  • Nach 2 Minuten brennen diese Zellen nicht mehr und das Feuer breitet sich auf alle benachbarten Zellen aus, aber [0,2], [2,0], [2,4], [0,4] brauchen nur noch 1 Minute, um zu brennen
  • Nach 1 Minute werden diese Zellen verbrannt und die Zelle breitet sich zu ihren benachbarten Zellen aus.
  • Nach einer weiteren Minute brennen die restlichen Zellen aus Schritt 3 und das Feuer breitet sich auf die benachbarten Zellen aus (die bereits verbrannt sind, sodass nichts passiert).
  • Nach einer letzten Minute brennt das Feuer auf dem gesamten Gelände.

Die Lösung für diesen Fall sind also 8 Minuten. Wenn das Feuer in der Zelle ganz oben links beginnt [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

Die Gesamtzeit beträgt jetzt also 10 Minuten.

Die Herausforderung

Schreiben Sie bei einer NxM-Matrix (N> 0, M> 0) von ganzzahligen Werten, die die Zeit darstellen, die jede Zelle benötigt, um vollständig verbraucht zu werden, das kürzeste Programm / die kürzeste Funktion, die diese Matrix benötigt, und ein Paar ganzer Zahlen mit der Position, an der das Feuer beginnt , und gibt die Zeit aus, die das Feuer benötigt, um das gesamte Gelände vollständig zu verbrauchen.

  • Jede Zelle hat eine positive Brenndauer (ungleich Null). Sie können keinen Maximalwert für die Zellen annehmen.
  • Die Matrix muss weder quadratisch noch symmetrisch sein.
  • Die Matrix kann 0-indiziert oder 1-indiziert sein, wie Sie möchten.
  • Die Position kann als ein einzelner Parameter mit einem Tupel von ganzen Zahlen angegeben werden, zwei getrennte Parameter in jedem anderen vernünftigen Format.
  • Die Abmessungen der Matrix können nicht als Eingabeparameter angegeben werden.
  • Sie müssen nicht jeden Zwischenschritt ausgeben, sondern nur die angeforderte Zeit. Aber ich werde mich nicht beschweren, wenn die Schritte in irgendeiner Weise visualisiert werden.

Ein anderes Beispiel:

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

Das ist , also kann das kürzeste Programm für jede Sprache gewinnen!


1
@LeanderMoesinger es muss mit jeder Matrix funktionieren. Was ich meine ist, dass Ihr Programm oder Ihre Funktion die Dimensionen der Matrix nicht als Eingabeparameter akzeptieren kann, aber natürlich können Sie diese Dimensionen in Ihrem Code berechnen.
Charlie

Kann die Eingabe als einzelne Zahl in Spalten-Hauptreihenfolge verwendet werden ? Das heißt, die Matrixeinträge werden nummeriert, dann quer
Luis Mendo

1
@ LuisMendo ja, natürlich. Beachten Sie jedoch, dass die Brenndauer jeder Zelle größer als 9 sein kann, wenn dies für den Teil "Einzelzahl" von Bedeutung ist.
Charlie

Vielen Dank. Nein, das spielt keine Rolle. Ich meinte eine einzelne Nummer, aber möglicherweise mit mehreren Ziffern. Die Zahl reicht von 1bisM*N
Luis Mendo

Antworten:


12

Matlab, 235 257 190 182 178 Bytes

Eingabe: Matrix A, 1x2-Vektor pmit den Startkoordinaten.

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)

Erläuterung:

Anstatt die Ausbreitung des Feuers zu simulieren, können wir dies auch als ein Problem "Finde den längsten kürzesten Weg" verstehen. Die Matrix wird in einen gewichteten gerichteten Graphen umgewandelt. Die Gewichte der Pfade zu einem einzelnen Knoten entsprechen der Zeit zum Brennen des Knotens. ZB für eine Matrix

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

Wir erhalten den zusammenhängenden Graphen:

Graph

Wobei Knoten 1 das obere linke Matrixelement und Knoten 12 das untere rechte Element ist. Bei gegebenen Startkoordinaten pwird der kürzeste Weg zu allen anderen Knoten berechnet. Dann entspricht die Länge des längsten Pfades dieser kürzesten Pfade + die Zeit zum Brennen des Anfangsknotens der Zeit zum Brennen der gesamten Matrix.

Ungolfed und kommentierte Version mit Beispielstartwerten:

% 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
Willkommen bei PPCG!
Stephen

Sehr nette Ansprache und sehr gut erklärt!
Charlie

Hey, da dies mein erstes Golfspiel ist, muss ich fragen, ob es in Ordnung ist, dass ich ;nach jeder Zeile das weggelassen habe . In Matlab verhindern diese, dass die Ergebnisse der einzelnen Befehle in der Konsole angezeigt werden. Derzeit ist der Code SEHR gesprächig und spammt die Konsole. Aber da dies kein strikter Fehlerzustand ist, habe ich es so gehalten. Aber es spielt keine Rolle, es ist nur 4 Bytes
Leander Moesinger

1
@LeanderMoesinger Geht der Spam in den gleichen Ausgabebereich wie Ihre Programmausgabe? Wenn der Spam beispielsweise in STDERR oder ein gleichwertiges Element und die Ausgabe in STDOUT oder ein gleichwertiges Element eingeht, sollten Sie diese entfernen. Wenn sie beide am selben Ort ausgeben, würde ich es nicht wissen.
Stephen

@ Es ist ein anderer Ausgabebereich, aber ich kann ihn insgesamt vermeiden, indem ich alles auf eine Zeile setze. Danke für die Klarstellung!
Leander Moesinger

9

JavaScript (ES6), 156 152 146 144 143 Bytes

Dank Kevin Cruijssen 1 Byte gespeichert

Eine eher naive Umsetzung. Nimmt Eingaben in Curry-Syntax vor (a)(s), wobei a ein 2D-Array und s ein Array aus zwei Ganzzahlen [ x, y ] ist, die die 0-basierten Koordinaten der Startposition darstellen.

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)

Formatiert und kommentiert

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

Testfälle


==0kann golfen werden, <1wenn ich mich nicht irre.
Kevin Cruijssen

1
@ KevinCruijssen Dies ist in der Tat sicher, wie undefined<1falsch ist. Vielen Dank!
Arnauld

8

Oktave, 67 Bytes

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

Probieren Sie es online!

Um Zwischenergebnisse zu visualisieren, können Sie dies versuchen!

Eine Funktion, die als Eingabematrix für das Terrain aund die Anfangskoordinate eine Matrix aus 0 und 1 mit der gleichen Größe wie das Terrain verwendet.

Eigentlich muss endfunctiondas Beispiel nicht ausgeführt werden, um es hinzuzufügen.

Erläuterung:

Wenden Sie wiederholt morphologische Bilddilatation auf das Gelände an und subtrahieren Sie die verbrannten Bereiche davon.

Ungolfed Antwort:

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

Dies ist eine gute Antwort, aber möglicherweise zählt der Algorithmus den Anfangszustand als Schritt und gibt in Ihrem Beispiel 11 statt 10 zurück. Wenn ich die ursprüngliche Zelle auf [3 3] ändere, ist das Ergebnis 9 statt 8.
Charlie

@ Carlos Alejo OH, meine schlechte. Antwort aktualisiert geändert n=1zu n=0.
rahnema1

7

MATL , 26 25 Bytes

Ich wollte unbedingt noch mehr Antworten in den Golfsprachen sehen

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

Eingabeformat ist:

  • Die erste Eingabe ist eine Matrix, die ;als Zeilentrennzeichen verwendet wird.

  • Die zweite Eingabe ist eine einzelne Zahl, die einen Eintrag der Matrix in einer auf 1 basierenden Spalten-Hauptreihenfolge adressiert (gemäß der Abfrage zulässig). Beispielsweise ist die Spaltenhauptkoordinate jedes Eintrags in einer 3 × 4-Matrix durch gegeben

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

    So entsprechen zum Beispiel 1-basierte Koordinaten (2,2) 5.

Probieren Sie es online! Oder überprüfen Sie alle Testfälle .

Erläuterung

Der Code enthält eine Liste der brennenden Einträge. Bei jeder Iteration werden alle Einträge dieser Liste dekrementiert. Wenn ein Eintrag Null erreicht, werden die benachbarten Einträge zur Liste hinzugefügt. Um Bytes zu sparen, werden Einträge, die Null erreichen, nicht aus der Liste entfernt. Stattdessen "brennen" sie mit negativen Werten weiter. Die Schleife wird verlassen, wenn keiner der Einträge positive Werte hat.

Sehen Sie sich das Programm Schritt für Schritt mit leicht verändertem Code an.

Kommentierter Code:

`      % 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
Das nenne ich einen Funktionscode! :-)
Charlie

4

Python 2 , 170 Bytes

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

Probieren Sie es online!


4

Python 3 , 277 266 Bytes

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)

Probieren Sie es online!

Definiert eine Funktion f, die eine 2D-Matrix und ein Tupel von Punkten aufnimmt. Das erste , was die Funktion hat, einen Satz von Tupel definiert den anfänglichen tuple Wert enthält gebenen: p={s}. Die Funktion geht dann jedes Tupel von Punkten durch pund subtrahiert man diesem Punkt eins von der Matrix , es sei denn, der Wert ist bereits Null. Es wird dann durchgeschleiftm alle Punkte mit dem Wert Null erneut gesucht und die vier Nachbarn dieses Punkts zur Menge hinzugefügt p. Aus diesem Grund habe ich mich für die Verwendung eines Sets entschieden, da Sets in Python keine doppelten Werte zulassen (was die Subtraktion sehr verkorksen würde). Leider müssen list[-1] == list[len(list)-1]die Indizes aufgrund von Listenindexumbrüchen (zB :) eingeschränkt werden, damit sie nicht negativ werden und die falschen Koordinaten hinzufügen p.

Nichts besonderes, gewöhnungsbedürftig. Hier gibt es definitiv Raum für Verbesserungen, ich werde weiter daran arbeiten.


Könnten Sie ein Ausführungsbeispiel bei Try it online schreiben, damit wir alle Ihren Code testen können?
Charlie

@CarlosAlejo Natürlich hat es den Beitrag hinzugefügt.
MooseOnTheRocks

4

APL (Dyalog) , 93 66 57 Bytes

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

Probieren Sie es online! oder Visualisiere es online!


Diese Funktion nimmt die Geländematrix als rechtes Argument und die Koordinaten (1-basiert) des ersten Feuers als linkes Argument. Gibt die Anzahl der Minuten zurück, die benötigt werden, um alles zu brennen.


Aktualisierung

Endlich einen Weg gefunden, um die Spread-Funktion abzuspielen.
* Seufz * es wäre so viel einfacher, wenn die Welt wäre toroid wäre .


TIO hat gerade ein Upgrade auf Dyalog 16.0 durchgeführt , was bedeutet, dass wir jetzt den glänzenden neuen Schablonenoperator haben


Sehr nette Antwort! Die Zwischenausgabe hilft, den Fortschritt zu visualisieren!
Charlie

2

Python 2 , 268 Bytes

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

Probieren Sie es online!

Iterieren Sie rekursiv über Zeitschritte, in denen die Anzahl jeder Kachel verringert wird, wenn sie im Wesentlichen an eine 0 angrenzt. Sehr einfacher Algorithmus, der meines Erachtens immer noch für eine boolesche Effizienz verwendet werden kann ...

* Anmerkung: mein 'Online ausprobieren!' Der Code enthält die Bonus-Debug-Protokollierung in der Fußzeile. Ich beobachte gerne den Fortschritt des Algorithmus.


2

Haskell , 138 133 Bytes

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

Probieren Sie es online!

Angenommen, die Eingabe ist eine Liste von ((x, y), Zelle). 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)]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.