<?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();
?>