Per gli appassionati di Pathfinder come me capita di fare il master e voler proporre qualche simpatico labirinto ai propri avventurieri. Online si trovano già moltissimi generatori di dungeon, però ho deciso di svagarmi un po’ questo sabato pomeriggio creando uno tutto mio in PHP.
Questo è il risultato (si rigenera ad ogni refresh della pagina):
Vediamo il codice sufficiente per ottenerlo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
<?php // per evitare eventuali avvisi nell'output jpeg error_reporting(0); // diamo qualche secondo di più per generare labirinti più grandi set_time_limit(120); class Labirinto { // dimensione mappa predefinita in larghezza (w) e altezza (h) private $w = 800; private $h = 800; // colore del labirinto private $fgcolor = array(255,255,255); // colore dello sfondo del labirinto private $bgcolor = array(0,0,0); // dimensione di ogni quadretto private $pixel = 4; // ingresso nel labirinto come coordinate w, h; oppure x, y private $sw = 1; private $sh = 0; // massimo numero di iterazioni per generare un singolo percorso private $max = 100000; // numero di iterazioni attuali private $cont = 0; // matrice della mappa base con ingresso predefinito, array di array private $map = array(array(0,1,0)); // variabile per tenere conto dell'uscita dal labirinto // quando viene disegnata un'uscita la variabile diventa TRUE private $exit = false; function __construct() { } // impostazione variabili predefinite, larghezza, altezza e dimensione quadretti function set($w,$h,$pixel = 4) { $this->w = $w; $this->h = $h; $this->pixel = $pixel; } // genera labirinto function get() { $this->max = $this->w * $this->h; $this->build(); $this->draw(); } // costruisci labirinto function build() { // crea il primo ramo a partire dalla posizione predefinita $this->go($this->sw,$this->sh); // ripassa il labirinto aggiungendo rami aggiuntivi // ripetiamo per 4 volte (niente di speciale in questo numero) per migliorare il riempimento for( $k = 0; $k < 4; $k++ ) { for( $r = 0; $r < $this->h; $r++ ) { for( $c = 0; $c < $this->w; $c++ ) { if( $this->map[$r][$c] ) { $this->go($c,$r); } } } } } // genera un ramo del labirinto function go( $w, $h ) { $dest = -1; $try = 0; while( true ) { if( $dest < 0 ) { $dest = rand(0,3); } else { $dest = (++$dest)%4; } $try++; switch( $dest ) { case 0: $dw = $w; $dh = $h-1; break; case 1: $dw = $w+1; $dh = $h; break; case 2: $dw = $w; $dh = $h+1; break; case 3: $dw = $w-1; $dh = $h; break; } if( $this->map[$dh][$dw] == 0 && ( ($dw > 0 && $dw < $this->w-1 && $dh > 0 && $dh < $this->h-1) || ($this->exit == false && $dh == $this->h-1)) ) { if( $this->check($dw,$dh,1,0,0,0) || $this->check($dw,$dh,0,1,0,0) || $this->check($dw,$dh,0,0,1,0) || $this->check($dw,$dh,0,0,0,1) ) { if( $this->ctrl($dw,$dh) ) { $this->map[$dh][$dw] = 1; $this->cont++; if( $this->exit == false && $dh == $this->h-1 ) $this->exit = true; if( $this->cont < $this->max ) $this->go($dw,$dh); break; } } } if( $this->check($w,$h,1,1,1,1) ) break; if( $this->check($w,$h,0,1,1,1) && $dh == 0 ) break; if( $this->check($w,$h,1,1,0,1) && $dh == $this->h-1 ) break; if( $this->check($w,$h,1,1,1,0) && $dw == 0 ) break; if( $this->check($w,$h,1,0,1,1) && $dw == $this->w-1 ) break; if( $try > 4 ) break; } } // verifichiamo la posizione di passaggi di celle esistenti rispetto a quella attuale // parametri: width, height, top, right, bottom, left // impostiamo quindi su width e height la posizione // impostiamo su t, r, b, l i valori a 0 o 1, in base che ci debbano o meno essere delle celle function check($w,$h,$t,$r,$b,$l) { return $this->map[$h-1][$w] == $t && $this->map[$h][$w+1] == $r && $this->map[$h+1][$w] == $b && $this->map[$h][$w-1] == $l; } // verifichiamo la posizione di celle esistenti in diagonale, secondo lo schema top-left, top-right, bottom-right, bottom-left function checkd($w,$h,$tl,$tr,$br,$bl) { return $this->map[$h-1][$w-1] == $tl && $this->map[$h-1][$w+1] == $tr && $this->map[$h+1][$w-1] == $br && $this->map[$h+1][$w+1] == $bl; } // controllo aggiunti sullo stato della singola cella, verifichiamo che si possa procedere e non ci siano accanti celle piene function ctrl($w,$h) { $ctrl = true; $ctrl = $ctrl && !($this->map[$h][$w-1] == 0 && $this->map[$h-1][$w-1] == 1 && $this->map[$h-1][$w] == 0); $ctrl = $ctrl && !($this->map[$h-1][$w] == 0 && $this->map[$h-1][$w+1] == 1 && $this->map[$h][$w+1] == 0); $ctrl = $ctrl && !($this->map[$h][$w+1] == 0 && $this->map[$h+1][$w+1] == 1 && $this->map[$h+1][$w] == 0); $ctrl = $ctrl && !($this->map[$h+1][$w] == 0 && $this->map[$h+1][$w-1] == 1 && $this->map[$h][$w-1] == 0); return $ctrl; } // disegna labirinto su output jpeg function draw() { header("Content-Type: image/png"); $im = @imagecreate($this->w * $this->pixel, $this->h * $this->pixel) or die("Cannot Initialize new GD image stream"); $background_color = imagecolorallocate($im, $this->bgcolor[0], $this->bgcolor[1], $this->bgcolor[2]); $this->map($im); imagejpeg($im,NULL,100); imagedestroy($im); } // prepara la mappa in base ai valori 0 e 1, 0 celle vuote, 1 celle del labirinto function map(&$im) { $fc = @imagecolorallocate($im, $this->fgcolor[0], $this->fgcolor[1], $this->fgcolor[2]); $bc = @imagecolorallocate($im, $this->bgcolor[0], $this->bgcolor[1], $this->bgcolor[2]); for( $r = 0; $r < $this->h; $r++ ) { for( $c = 0; $c < $this->w; $c++ ) { for( $ph = 0; $ph < $this->pixel; $ph++ ) for( $pw = 0; $pw < $this->pixel; $pw++ ) if( $this->map[$r][$c] ) { imagesetpixel($im,$c*$this->pixel+$ph,$r*$this->pixel+$pw,$fc); } else { imagesetpixel($im,$c*$this->pixel+$ph,$r*$this->pixel+$pw,$bc); } } } } } $lab = new Labirinto(); $lab->set(30,30,16); $lab->get(); ?> |
Le impostazioni nell’esempio sono le medesime per ottenere l’immagine di sopra.