Kirjoittaja: J.J.
Kirjoitettu: 03.05.2005 – 03.05.2005
Tagit: koodi näytille, vinkki
Tämä ohjelma generoi labyrintin, jossa on ainoastaan yksi ratkaisu. Umpikujat ovat toisinaan todella pitkiä, tai mitättömän lyhyitä, mutta en enään jaksanut alkaa miettiä millä sitä saisi yksinkertaisesti säädettyä.
Keltainen ruutu tarkoittaa aloituspistettä ja punainen loppua, joka on sijoitettu aloituspisteestä niin kauaksi kuin mahdollista (tämä lienee helppo muuttaa jos kokee sen tarpeelliseksi).
Edit: Lisäsin muunnellun version vanhasta ohjelmasta. Uusi versio tekee klassisemman näköisiä ja ainakin omasta mielestäni parempia labyrintteja. Lisäksi ohjelma kopioi labyrintista tiedoston (jos siitä jollekin on hyötyä).
' LABY.BAS -JJP
DECLARE SUB Generate ()
DECLARE SUB DisplayMaze ()
DECLARE FUNCTION IsFloor% (x%, y%)
DECLARE FUNCTION Peruuta% (x%, y%)
DECLARE FUNCTION Rand% (a%, b%)
DECLARE SUB Rotate (d%)
DECLARE SUB Siirry (x%, y%, d%)
DECLARE FUNCTION VoikoPiirtaa% (x%, y%, d%)
DEFINT A-Z
CONST sizeX = 80
CONST sizeY = 50
CONST Seina = 1
CONST Lattia = 7
CONST Alku = 14
CONST Loppu = 4
CONST TempTile = -1
CLS
SCREEN 13
DIM SHARED Maze(1 TO sizeX, 1 TO sizeY)
RANDOMIZE TIMER
aika! = TIMER
Generate
aika! = TIMER - aika!
DisplayMaze
PRINT "Aikaa labyrintin luontiin kului:"; aika!; "s."
SUB DisplayMaze
tx = FIX(320 / sizeX)
ty = FIX(200 / sizeY)
IF tx < ty THEN ty = tx ELSE tx = ty
FOR x = 1 TO sizeX
FOR y = 1 TO sizeY
LINE (x * tx - tx, y * ty - ty)-(x * tx, y * ty), Maze(x, y), BF
NEXT y
NEXT x
DO UNTIL NOT INKEY$ = "": LOOP
END SUB
SUB Generate
FOR x = 1 TO sizeX
FOR y = 1 TO sizeY
Maze(x, y) = Seina
NEXT y
NEXT x
Matka = 0
Ennatys = 0
startX = Rand(2, sizeX - 1)
startY = Rand(2, sizeY - 1)
x = startX
y = startY
Maze(startX, startY) = TempTile
DO
d = Rand(1, 2)
IF Rand(0, 1) THEN d = d * -1
FOR Kokeile = 1 TO 4
IF VoikoPiirtaa(x, y, d) THEN
Siirry x, y, d
Matka = Matka + 1
EiOnnistu = 0
EXIT FOR
ELSE
EiOnnistu = 1
Rotate d
END IF
NEXT Kokeile
IF EiOnnistu THEN
IF Matka > Ennatys THEN
Ennatys = Matka
EnnX = x
EnnY = y
END IF
IF Peruuta(x, y) = 0 THEN
Maze(startX, startY) = Alku
Maze(EnnX, EnnY) = Loppu
EXIT SUB
END IF
Matka = Matka - 1
END IF
LOOP
END SUB
FUNCTION IsFloor (x, y)
IF Maze(x, y) = Lattia OR Maze(x, y) = TempTile THEN IsFloor = 1 ELSE IsFloor = 0
END FUNCTION
FUNCTION Peruuta (x, y)
Maze(x, y) = Lattia
IF Maze(x - 1, y) = TempTile THEN x = x - 1: Peruuta = 1: EXIT FUNCTION
IF Maze(x + 1, y) = TempTile THEN x = x + 1: Peruuta = 1: EXIT FUNCTION
IF Maze(x, y + 1) = TempTile THEN y = y + 1: Peruuta = 1: EXIT FUNCTION
IF Maze(x, y - 1) = TempTile THEN y = y - 1: Peruuta = 1: EXIT FUNCTION
Peruuta = 0
END FUNCTION
FUNCTION Rand (a, b)
IF a > b THEN
High = a + 1
Low = b
ELSE
High = b + 1
Low = a
END IF
Dif = High - Low
Rand = INT(RND * Dif) + Low
END FUNCTION
SUB Rotate (d)
SELECT CASE (d)
CASE 1
d = -1
EXIT SUB
CASE -1
d = 2
EXIT SUB
CASE 2
d = -2
EXIT SUB
CASE -2
d = 1
EXIT SUB
END SELECT
END SUB
SUB Siirry (x, y, d)
SELECT CASE (d)
CASE -1
y = y - 1
CASE 1
y = y + 1
CASE -2
x = x - 1
CASE 2
x = x + 1
END SELECT
Maze(x, y) = TempTile
END SUB
FUNCTION VoikoPiirtaa (x, y, d)
SELECT CASE (d)
CASE 1
IF y + 1 >= sizeY THEN VoikoPiirtaa = 0: EXIT FUNCTION
FOR scan = -1 TO 1
IF IsFloor(x + scan, y + 2) THEN VoikoPiirtaa = 0: EXIT FUNCTION
IF IsFloor(x + scan, y + 1) THEN VoikoPiirtaa = 0: EXIT FUNCTION
NEXT scan
CASE -1
IF y - 1 <= 1 THEN VoikoPiirtaa = 0: EXIT FUNCTION
FOR scan = -1 TO 1
IF IsFloor(x + scan, y - 2) THEN VoikoPiirtaa = 0: EXIT FUNCTION
IF IsFloor(x + scan, y - 1) THEN VoikoPiirtaa = 0: EXIT FUNCTION
NEXT scan
CASE 2
IF x + 1 >= sizeX THEN VoikoPiirtaa = 0: EXIT FUNCTION
FOR scan = -1 TO 1
IF IsFloor(x + 2, y + scan) THEN VoikoPiirtaa = 0: EXIT FUNCTION
IF IsFloor(x + 1, y + scan) THEN VoikoPiirtaa = 0: EXIT FUNCTION
NEXT scan
CASE -2
IF x - 1 <= 1 THEN VoikoPiirtaa = 0: EXIT FUNCTION
FOR scan = -1 TO 1
IF IsFloor(x - 2, y + scan) THEN VoikoPiirtaa = 0: EXIT FUNCTION
IF IsFloor(x - 1, y + scan) THEN VoikoPiirtaa = 0: EXIT FUNCTION
NEXT scan
END SELECT
VoikoPiirtaa = 1
END FUNCTION' LABY.BAS v2 -JJP
DECLARE SUB KirjoitaTiedosto ()
DECLARE SUB Generate ()
DECLARE SUB DisplayMaze ()
DECLARE FUNCTION IsFloor% (x%, y%)
DECLARE FUNCTION Peruuta% (x%, y%)
DECLARE FUNCTION Rand% (a%, b%)
DECLARE SUB Rotate (d%)
DECLARE SUB Siirry (x%, y%, d%)
DECLARE FUNCTION VoikoPiirtaa% (x%, y%, d%)
DEFINT A-Z
CONST sizeX = 81
CONST sizeY = 61
CONST Seina = 1
CONST Lattia = 0
CONST Alku = 14
CONST Loppu = 4
CONST TempTile = -1
CONST Tiedosto$ = "LABY.DAT"
CLS
SCREEN 12
DIM SHARED Maze(1 TO sizeX, 1 TO sizeY)
RANDOMIZE TIMER
aika! = TIMER
Generate
aika! = TIMER - aika!
DisplayMaze
PRINT "Aikaa labyrintin luontiin kului:"; aika!; "s."
KirjoitaTiedosto
END
SUB DisplayMaze
tx = FIX(640 / sizeX)
ty = FIX(480 / sizeY)
IF tx < ty THEN ty = tx ELSE tx = ty
FOR x = 1 TO sizeX
FOR y = 1 TO sizeY
LINE (x * tx - tx, y * ty - ty)-(x * tx, y * ty), Maze(x, y), BF
NEXT y
NEXT x
DO UNTIL NOT INKEY$ = "": LOOP
END SUB
SUB Generate
FOR x = 1 TO sizeX
FOR y = 1 TO sizeY
Maze(x, y) = Seina
NEXT y
NEXT x
Matka = 0
Ennatys = 0
startX = Rand(1, FIX((sizeX - 1) / 2)) * 2
startY = Rand(1, FIX((sizeY - 1) / 2)) * 2
x = startX
y = startY
Maze(startX, startY) = TempTile
DO
d = Rand(1, 2)
IF Rand(0, 1) THEN d = d * -1
FOR Kokeile = 1 TO 4
IF VoikoPiirtaa(x, y, d) THEN
Siirry x, y, d
Matka = Matka + 1
EiOnnistu = 0
EXIT FOR
ELSE
EiOnnistu = 1
Rotate d
END IF
NEXT Kokeile
IF EiOnnistu THEN
IF Matka > Ennatys THEN
Ennatys = Matka
EnnX = x
EnnY = y
END IF
IF Peruuta(x, y) = 0 THEN
Maze(startX, startY) = Alku
Maze(EnnX, EnnY) = Loppu
EXIT SUB
END IF
Matka = Matka - 1
END IF
LOOP
END SUB
FUNCTION IsFloor (x, y)
IF Maze(x, y) = Lattia OR Maze(x, y) = TempTile THEN IsFloor = 1 ELSE IsFloor = 0
END FUNCTION
SUB KirjoitaTiedosto
OPEN Tiedosto$ FOR OUTPUT AS #1
FOR y = 1 TO sizeY
rivi$ = ""
FOR x = 1 TO sizeX
SELECT CASE (Maze(x, y))
CASE Seina
rivi$ = rivi$ + "Û"
CASE Lattia
rivi$ = rivi$ + "."
CASE Alku
rivi$ = rivi$ + "A"
CASE Loppu
rivi$ = rivi$ + "L"
END SELECT
NEXT x
PRINT #1, rivi$
NEXT y
CLOSE #1
END SUB
FUNCTION Peruuta (x, y)
Maze(x, y) = Lattia
IF Maze(x - 1, y) = TempTile THEN Maze(x - 1, y) = Lattia: x = x - 2: Peruuta = 1: EXIT FUNCTION
IF Maze(x + 1, y) = TempTile THEN Maze(x + 1, y) = Lattia: x = x + 2: Peruuta = 1: EXIT FUNCTION
IF Maze(x, y + 1) = TempTile THEN Maze(x, y + 1) = Lattia: y = y + 2: Peruuta = 1: EXIT FUNCTION
IF Maze(x, y - 1) = TempTile THEN Maze(x, y - 1) = Lattia: y = y - 2: Peruuta = 1: EXIT FUNCTION
Peruuta = 0
END FUNCTION
FUNCTION Rand (a, b)
IF a > b THEN
High = a + 1
Low = b
ELSE
High = b + 1
Low = a
END IF
Dif = High - Low
Rand = INT(RND * Dif) + Low
END FUNCTION
SUB Rotate (d)
SELECT CASE (d)
CASE 1
d = -1
EXIT SUB
CASE -1
d = 2
EXIT SUB
CASE 2
d = -2
EXIT SUB
CASE -2
d = 1
EXIT SUB
END SELECT
END SUB
SUB Siirry (x, y, d)
SELECT CASE (d)
CASE -1
Maze(x, y - 1) = TempTile
y = y - 2
CASE 1
Maze(x, y + 1) = TempTile
y = y + 2
CASE -2
Maze(x - 1, y) = TempTile
x = x - 2
CASE 2
Maze(x + 1, y) = TempTile
x = x + 2
END SELECT
Maze(x, y) = TempTile
END SUB
FUNCTION VoikoPiirtaa (x, y, d)
SELECT CASE (d)
CASE 1
IF y + 2 >= sizeY THEN VoikoPiirtaa = 0: EXIT FUNCTION
FOR scan = -1 TO 1
IF IsFloor(x + scan, y + 3) THEN VoikoPiirtaa = 0: EXIT FUNCTION
IF IsFloor(x + scan, y + 2) THEN VoikoPiirtaa = 0: EXIT FUNCTION
NEXT scan
CASE -1
IF y - 2 <= 1 THEN VoikoPiirtaa = 0: EXIT FUNCTION
FOR scan = -1 TO 1
IF IsFloor(x + scan, y - 3) THEN VoikoPiirtaa = 0: EXIT FUNCTION
IF IsFloor(x + scan, y - 2) THEN VoikoPiirtaa = 0: EXIT FUNCTION
NEXT scan
CASE 2
IF x + 2 >= sizeX THEN VoikoPiirtaa = 0: EXIT FUNCTION
FOR scan = -1 TO 1
IF IsFloor(x + 3, y + scan) THEN VoikoPiirtaa = 0: EXIT FUNCTION
IF IsFloor(x + 2, y + scan) THEN VoikoPiirtaa = 0: EXIT FUNCTION
NEXT scan
CASE -2
IF x - 2 <= 1 THEN VoikoPiirtaa = 0: EXIT FUNCTION
FOR scan = -1 TO 1
IF IsFloor(x - 3, y + scan) THEN VoikoPiirtaa = 0: EXIT FUNCTION
IF IsFloor(x - 2, y + scan) THEN VoikoPiirtaa = 0: EXIT FUNCTION
NEXT scan
END SELECT
VoikoPiirtaa = 1
END FUNCTIONOuto, mutta toimii hienosti :o
Värit vaihdoin toisinpäin, koska tuntui siltä että seinä oli aluetta josta pystyi kulkea.
Heh, minäkin katsoin katsoin ensin väärinpäin seinää ja lattiaa. :) Algoritmi generoi kivoja labyrintteja.
Yritin joskus thedä vastaavaa kun kaikenlaisten generointisysteemit kiinostaa, mutta ei siitä oikein tullut mitään. Nyt kuitenkin olen saanut yhden haaveeni vähän valmiimmaksi tekemällä Infinite Arenaan satunnaiskarttageneraattorin.
Minulla loi tyhmän labyrintin, josta pääsi helposti pisteestä a pisteeseen b. Ne oli vierekkäin eikä välissä ollut seinää.
Tohon kun viittisit vielä soveltaa tulostusmahdollisuuden, ettei tarviis koko ajan ruudun edessä kököttää. Onhan Print Screen-toiminto tietysti keksitty, mutta taas jos haluaa tulostaa isomman erän, niin huomaat kyllä homman hitauden. Toinen ehdotus olisi ukon luonti labyrinttiin.
Muuten kyllä ihan simppeli.