Jump to content

Recommended Posts

Hello, I wanna make a random grid map with mapWidth x mapHeight. I have 3 faults in this script.

#1. Broken paths (every square needed to be available)
#2. Walking in circles
#3. 2 arrays are made (see description in script)

In the image you see the broken paths (red lines), never able to go to every square. And the walking in circles (blue circle)

 

<?php

// Start Sessie
session_start();

// Map settings
$mapWidth		= 5;
$mapHeight		= 5;
$squareSize 	= 70;
$squareColor	= '#333';

// Content settings
$monster		= '&#129430;';
$treasure		= '&#127873;';
$start			= '&#128578;';
$exit			= '&#128274;';

// Make SVG files
$fontSize = $squareSize * 2;
$svg['center']	= '<rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />';
$svg['right']	= '<rect width="10%" height="20%" x="90%" y="40%" />';
$svg['bottom']	= '<rect width="20%" height="10%" x="40%" y="90%" />';
$svg['left']	= '<rect width="10%" height="20%" x="0" y="40%" />';
$svg['top']		= '<rect width="20%" height="10%" x="40%" y="0%" />';

// Function to count the values in a field
function count_values($arr)
{
	$counting = 0;
	foreach ($arr as $key => $val)
	{
		if (is_numeric($val))
		{
			$counting = $counting + $val;
		}
	}
	return $counting;
}

// Calculate Monsters
$monsters = ceil($mapHeight * $mapWidth * 0.16);

// Calculate Chests
$chests = floor($mapHeight * $mapWidth * 0.24);

// Check map size
if ($monsters == 0 || $chests == 0 || ($monsters + $chests + 2) > ($mapHeight * $mapWidth))
{
	print 'Please change your mapsize';
	exit;
}

// Create content
$squareChests	= array_fill(1, $chests, 'treasure');
$squareMonsters	= array_fill(1, $monsters, 'monster');
$squareStart	= array(1 => 'start');
$squareEnd		= array(1 => 'exit');

// Merge and mix all content
$fillbox = array_merge($squareChests, $squareMonsters, $squareStart, $squareEnd);
shuffle($fillbox);

/******************************\
	Start Grid Map
\******************************/
// Build column
for ($row=1; $row<=$mapHeight; $row++)
{
	// Build row
	for ($col=1; $col<=$mapWidth; $col++)
	{
		// Default settings
		$squareMaxTop		= 1;
		$squareMaxLeft		= 1;
		$squareMaxBottom	= 1;
		$squareMaxRight		= 1;

		// Top row has never a door at top
		if ($row == 1){ $squareMaxTop = 0; }
		// First column has never a door at left
		if ($col == 1){ $squareMaxLeft = 0; }
		// Last row has never a door at the bottom
		if ($row == $mapHeight){ $squareMaxBottom = 0; }
		// Last column has never a door at the right
		if ($col == $mapWidth){ $squareMaxRight = 0; }

		// Create random square with at least 1 door
		$randomSquare = array();
		while (count_values($randomSquare) < 1)
		{
			$randomSquare = array(
				'top'		=> rand(0, $squareMaxTop),
				'right'		=> rand(0, $squareMaxRight),
				'bottom'	=> rand(0, $squareMaxBottom),
				'left'		=> rand(0, $squareMaxLeft),
			);
		}
		$field[$row][$col] = $randomSquare;
	}
}

/******************************\
	Check each field = connected
\******************************/
foreach ($field as $row => $colArr)
{
	foreach ($colArr as $col => $door)
	{
		// Check top door is connected with previous top field
		$previousTop = $row - 1;
		$field[$previousTop][$col] = isset($field[$previousTop][$col]) ? $field[$previousTop][$col] : NULL;
		if ($field[$previousTop][$col] != NULL)
		{
			$field[$row][$col]['top'] = $field[$previousTop][$col]['bottom'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['top'] = 1;
				$field[$previousTop][$col]['bottom'] = 1;
			}
		} else {
			unset($field[$previousTop][$col]);
		}

		// Check right door is connected with next right field
		$nextRight = $col + 1;
		$field[$row][$nextRight] = isset($field[$row][$nextRight]) ? $field[$row][$nextRight] : NULL;
		if ($field[$row][$nextRight] != NULL)
		{
			$field[$row][$col]['right'] = $field[$row][$nextRight]['left'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['right'] = 1;
				$field[$row][$nextRight]['left'] = 1;
			}
		} else {
			unset($field[$row][$nextRight]);
		}

		// Check bottom door is connected with next bottom field
		$nextBottom = $row + 1;
		$field[$nextBottom][$col] = isset($field[$nextBottom][$col]) ? $field[$nextBottom][$col] : NULL;
		if ($field[$nextBottom][$col] != NULL)
		{
			$field[$row][$col]['bottom'] = $field[$nextBottom][$col]['top'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['bottom'] = 1;
				$field[$nextBottom][$col]['top'] = 1;
			}
		} else {
			unset($field[$nextBottom][$col]);
		}

		// Check left door is connected with previous left field
		$previousLeft = $col - 1;
		$field[$row][$previousLeft] = isset($field[$row][$previousLeft]) ? $field[$row][$previousLeft] : NULL;
		if ($field[$row][$previousLeft] != NULL)
		{
			$field[$row][$col]['left'] = $field[$row][$previousLeft]['right'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['left'] = 1;
				$field[$row][$previousLeft]['right'] = 1;
			}
		} else {
			unset($field[$row][$previousLeft]);
		}

		// Give dead end always a monster / treasure / Start or Exit
		if (count_values($field[$row][$col]) == 1 && count($fillbox) > 0)
		{
			$field[$row][$col]['content'] = $fillbox[0];
			array_shift($fillbox);
		} else {
			$field[$row][$col]['content'] = '';
		}
	}
}

/**************************************************\
	No idea (yet) why a zero and (mapHeight + 1)
	are created in height
	But for now delete these 2 with unset
\**************************************************/
unset($field[0]);
unset($field[round($mapHeight + 1)]);

/**************************************************\
	Check broken paths
	If broken path, add entry to random field in broken path
\**************************************************/
// How to do ?????

/**************************************************\
	Check walking circles
	If circle, remove one entry from random field in cicle path
\**************************************************/
// How to do ?????

// Set leftover content to random fields
while (count($fillbox) > 0)
{
	$x = rand(1, $mapHeight);
	$y = rand(1, $mapWidth);
	if ($field[$x][$y]['content'] == '')
	{
		$field[$x][$y]['content'] = $fillbox[0];
		array_shift($fillbox);
	}
}

/******************************\
	Show grid
\******************************/
foreach ($field as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		// Create and show image SVG
		$image = '<svg width="'.$squareSize.'px" height="'.$squareSize.'px" fill="'.$squareColor.'" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL;
		$image .= '	'.$svg['center'].PHP_EOL; // Center square
		// The doors
		if ($colArr['top']		== 1) { $image .= '	'.$svg['top'].PHP_EOL; }
		if ($colArr['right']	== 1) { $image .= '	'.$svg['right'].PHP_EOL; }
		if ($colArr['bottom']	== 1) { $image .= '	'.$svg['bottom'].PHP_EOL; }
		if ($colArr['left']		== 1) { $image .= '	'.$svg['left'].PHP_EOL; }
		// The text (content)
		$text = '';
		if ($colArr['content'] == 'monster'){ $text = $monster; }
		if ($colArr['content'] == 'treasure'){ $text = $treasure; }
		if ($colArr['content'] == 'start'){ $text = $start; }
		if ($colArr['content'] == 'exit'){ $text = $exit; }
		$image .= '	<text x="50%" y="50% "textLength="100%" font-size="'.$fontSize.'%" dominant-baseline="middle" text-anchor="middle">'.$text.'</text>'.PHP_EOL;
		$image .= '</svg>'; // Close SVG

		print $image; // Show image
	}
	print '<br>'; // Next row
}

?>

 

broken-path-walking-circles.png

Edited by Elvium
Add picture as example
Link to comment
https://forums.phpfreaks.com/topic/323188-create-random-grid-map/
Share on other sites

Problem #3 found, here the new script

<?php

// Start Sessie
session_start();

// Map settings
$mapWidth		= 5;
$mapHeight		= 5;
$squareSize 	= 70;
$squareColor	= '#333';

// Content settings
$monster		= '&#129430;';
$treasure		= '&#127873;';
$start			= '&#128578;';
$exit			= '&#128274;';

// Make SVG files
$fontSize = $squareSize * 2;
$svg['center']	= '<rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />';
$svg['right']	= '<rect width="10%" height="20%" x="90%" y="40%" />';
$svg['bottom']	= '<rect width="20%" height="10%" x="40%" y="90%" />';
$svg['left']	= '<rect width="10%" height="20%" x="0" y="40%" />';
$svg['top']		= '<rect width="20%" height="10%" x="40%" y="0%" />';

// Function to count the values in a field
function count_values($arr)
{
	$counting = 0;
	foreach ($arr as $key => $val)
	{
		if (is_numeric($val))
		{
			$counting = $counting + $val;
		}
	}
	return $counting;
}

// Calculate Monsters
$monsters = ceil($mapHeight * $mapWidth * 0.16);

// Calculate Chests
$chests = floor($mapHeight * $mapWidth * 0.24);

// Check map size
if ($monsters == 0 || $chests == 0 || ($monsters + $chests + 2) > ($mapHeight * $mapWidth))
{
	print 'Please change your mapsize';
	exit;
}

// Create content
$squareChests	= array_fill(1, $chests, 'treasure');
$squareMonsters	= array_fill(1, $monsters, 'monster');
$squareStart	= array(1 => 'start');
$squareEnd		= array(1 => 'exit');

// Merge and mix all content
$fillbox = array_merge($squareChests, $squareMonsters, $squareStart, $squareEnd);
shuffle($fillbox);

/******************************\
	Start Grid Map
\******************************/
// Build column
for ($row=1; $row<=$mapHeight; $row++)
{
	// Build row
	for ($col=1; $col<=$mapWidth; $col++)
	{
		// Default settings
		$squareMaxTop		= 1;
		$squareMaxLeft		= 1;
		$squareMaxBottom	= 1;
		$squareMaxRight		= 1;

		// Top row has never a door at top
		if ($row == 1){ $squareMaxTop = 0; }
		// First column has never a door at left
		if ($col == 1){ $squareMaxLeft = 0; }
		// Last row has never a door at the bottom
		if ($row == $mapHeight){ $squareMaxBottom = 0; }
		// Last column has never a door at the right
		if ($col == $mapWidth){ $squareMaxRight = 0; }

		// Create random square with at least 1 door
		$randomSquare = array();
		while (count_values($randomSquare) < 1)
		{
			$randomSquare = array(
				'top'		=> rand(0, $squareMaxTop),
				'right'		=> rand(0, $squareMaxRight),
				'bottom'	=> rand(0, $squareMaxBottom),
				'left'		=> rand(0, $squareMaxLeft),
			);
		}
		$field[$row][$col] = $randomSquare;
	}
}

/******************************\
	Check each field = connected
\******************************/
foreach ($field as $row => $colArr)
{
	foreach ($colArr as $col => $door)
	{
		// Check top door is connected with previous top field
		$previousTop = $row - 1;
		$field[$previousTop][$col] = isset($field[$previousTop][$col]) ? $field[$previousTop][$col] : NULL;
		if ($field[$previousTop][$col] != NULL)
		{
			$field[$row][$col]['top'] = $field[$previousTop][$col]['bottom'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['top'] = 1;
				$field[$previousTop][$col]['bottom'] = 1;
			}
		} else {
			unset($field[$previousTop]);
		}

		// Check right door is connected with next right field
		$nextRight = $col + 1;
		$field[$row][$nextRight] = isset($field[$row][$nextRight]) ? $field[$row][$nextRight] : NULL;
		if ($field[$row][$nextRight] != NULL)
		{
			$field[$row][$col]['right'] = $field[$row][$nextRight]['left'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['right'] = 1;
				$field[$row][$nextRight]['left'] = 1;
			}
		} else {
			unset($field[$row][$nextRight]);
		}

		// Check bottom door is connected with next bottom field
		$nextBottom = $row + 1;
		$field[$nextBottom][$col] = isset($field[$nextBottom][$col]) ? $field[$nextBottom][$col] : NULL;
		if ($field[$nextBottom][$col] != NULL)
		{
			$field[$row][$col]['bottom'] = $field[$nextBottom][$col]['top'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['bottom'] = 1;
				$field[$nextBottom][$col]['top'] = 1;
			}
		} else {
			unset($field[$nextBottom]);
		}

		// Check left door is connected with previous left field
		$previousLeft = $col - 1;
		$field[$row][$previousLeft] = isset($field[$row][$previousLeft]) ? $field[$row][$previousLeft] : NULL;
		if ($field[$row][$previousLeft] != NULL)
		{
			$field[$row][$col]['left'] = $field[$row][$previousLeft]['right'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['left'] = 1;
				$field[$row][$previousLeft]['right'] = 1;
			}
		} else {
			unset($field[$row][$previousLeft]);
		}

		// Give dead end always a monster / treasure / Start or Exit
		if (count_values($field[$row][$col]) == 1 && count($fillbox) > 0)
		{
			$field[$row][$col]['content'] = $fillbox[0];
			array_shift($fillbox);
		} else {
			$field[$row][$col]['content'] = '';
		}
	}
}

/**************************************************\
	Check broken paths
	If broken path, add entry to random field in broken path
\**************************************************/
// How to do ?????

/**************************************************\
	Check walking circles
	If circle, remove one entry from random field in cicle path
\**************************************************/
// How to do ?????

// Set leftover content to random fields
while (count($fillbox) > 0)
{
	$x = rand(1, $mapHeight);
	$y = rand(1, $mapWidth);
	if ($field[$x][$y]['content'] == '')
	{
		$field[$x][$y]['content'] = $fillbox[0];
		array_shift($fillbox);
	}
}

/******************************\
	Show grid
\******************************/
foreach ($field as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		// Create and show image SVG
		$image = '<svg width="'.$squareSize.'px" height="'.$squareSize.'px" fill="'.$squareColor.'" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL;
		$image .= '	'.$svg['center'].PHP_EOL; // Center square
		// The doors
		if ($colArr['top']		== 1) { $image .= '	'.$svg['top'].PHP_EOL; }
		if ($colArr['right']	== 1) { $image .= '	'.$svg['right'].PHP_EOL; }
		if ($colArr['bottom']	== 1) { $image .= '	'.$svg['bottom'].PHP_EOL; }
		if ($colArr['left']		== 1) { $image .= '	'.$svg['left'].PHP_EOL; }
		// The text (content)
		$text = '';
		if ($colArr['content'] == 'monster'){ $text = $monster; }
		if ($colArr['content'] == 'treasure'){ $text = $treasure; }
		if ($colArr['content'] == 'start'){ $text = $start; }
		if ($colArr['content'] == 'exit'){ $text = $exit; }
		$image .= '	<text x="50%" y="50% "textLength="100%" font-size="'.$fontSize.'%" dominant-baseline="middle" text-anchor="middle">'.$text.'</text>'.PHP_EOL;
		$image .= '</svg>'; // Close SVG

		print $image; // Show image
	}
	print '<br>'; // Next row
}

?>

 

3 hours ago, Elvium said:

#1. Broken paths (every square needed to be available)

the code is doing what you have instructed it to do: create at least one door. Shouldn't you create at least 4 doors? the logic is a problem. Let's hack the script and see what happens.

/******************************\
	Start Grid Map
\******************************/
$squareMaxTop		= 1;
$squareMaxLeft		= 1;
$squareMaxBottom	= 1;
$squareMaxRight		= 1;

// Build column
for ($row=1; $row<=$mapHeight; $row++)
{
	// Build row
	for ($col=1; $col<=$mapWidth; $col++)
	{
		// Default settings makes no difference here

		// Create random square with at least 1 door
		$randomSquare = array();
		while (count_values($randomSquare) < 4)
		{
			$randomSquare = array(
				'top'		=> rand(0, $squareMaxTop),
				'right'		=> rand(0, $squareMaxRight),
				'bottom'	=> rand(0, $squareMaxBottom),
				'left'		=> rand(0, $squareMaxLeft),
			);
		}
		$field[$row][$col] = $randomSquare;
	}
}

You need to re-examine the border control (doors at edges) and tweak the code to include 4 doors/paths.

i had some time to find the errors in your logic which control the borders, so i tweaked your code to complete the 'grid'.

<?php

// Start Sessie
//session_start();

// Map settings
$mapWidth		= 5;
$mapHeight		= 5;
$squareSize 	= 70;
$squareColor	= '#333';

// Content settings
$monster		= '&#129430;';
$treasure		= '&#127873;';
$start			= '&#128578;';
$exit			= '&#128274;';

// Make SVG files
$fontSize = $squareSize * 2;
$svg['center']	= '<rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />';
$svg['right']	= '<rect width="10%" height="20%" x="90%" y="40%" />';
$svg['bottom']	= '<rect width="20%" height="10%" x="40%" y="90%" />';
$svg['left']	= '<rect width="10%" height="20%" x="0" y="40%" />';
$svg['top']		= '<rect width="20%" height="10%" x="40%" y="0%" />';

// Function to count the values in a field
function count_values($arr)
{
	$counting = 0;
	foreach ($arr as $key => $val)
	{
		if (is_numeric($val))
		{
			$counting = $counting + $val;
		}
	}
	return $counting;
}

// Calculate Monsters
$monsters = ceil($mapHeight * $mapWidth * 0.16);

// Calculate Chests
$chests = floor($mapHeight * $mapWidth * 0.24);

// Check map size
if ($monsters == 0 || $chests == 0 || ($monsters + $chests + 2) > ($mapHeight * $mapWidth))
{
	print 'Please change your mapsize';
	exit;
}

// Create content
$squareChests	= array_fill(1, $chests, 'treasure');
$squareMonsters	= array_fill(1, $monsters, 'monster');
$squareStart	= array(1 => 'start');
$squareEnd		= array(1 => 'exit');

// Merge and mix all content
$fillbox = array_merge($squareChests, $squareMonsters, $squareStart, $squareEnd);
shuffle($fillbox);

/******************************\
	Start Grid Map
\******************************/

$squareMaxTop		= 1;
$squareMaxLeft		= 1;
$squareMaxBottom	= 1;
$squareMaxRight		= 1;
		
// Build column
for ($row=1; $row<=$mapHeight; $row++)
{
	// Build row
	for ($col=1; $col<=$mapWidth; $col++)
	{
		$randomSquare = array();
		while (count_values($randomSquare) < 4)
		{
			$randomSquare = array(
				'top'		=> rand(0, $squareMaxTop),
				'right'		=> rand(0, $squareMaxRight),
				'bottom'	=> rand(0, $squareMaxBottom),
				'left'		=> rand(0, $squareMaxLeft),
			);
		}
		$field[$row][$col] = $randomSquare;
	}
}

/******************************\
	Show grid
\******************************/
foreach ($field as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		// Create and show image SVG
		$image = '<svg width="'.$squareSize.'px" height="'.$squareSize.'px" fill="'.$squareColor.'" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL;
		$image .= '	'.$svg['center'].PHP_EOL; // Center square
		// The doors
		if ($rowKey == 1) { $image .= '	'.PHP_EOL; } else { $image .= '	'.$svg['top'].PHP_EOL; }
		if ($colKey == 1) { $image .= '	'.PHP_EOL; } else { $image .= '	'.$svg['left'].PHP_EOL; }
		if ($rowKey == 5) { $image .= '	'.PHP_EOL; } else { $image .= '	'.$svg['bottom'].PHP_EOL; }
		if ($colKey == 5) { $image .= '	'.PHP_EOL; } else { $image .= '	'.$svg['right'].PHP_EOL; }

		// The text (content)
        $text = '';
		$image .= '	<text x="50%" y="50% "textLength="100%" font-size="'.$fontSize.'%" dominant-baseline="middle" text-anchor="middle">'.$text.'</text>'.PHP_EOL;
		$image .= '</svg>'; // Close SVG

		print $image; // Show image
	}
	print '<br>'; // Next row
}

?>

if you run the script, you will see a complete grid with all of the fields connected without border paths. I would not code a grid like this but it is your script. I hacked the code to make it work as you suggest (unless i have misinterpreted your request.)

I have tested the original script with my border control hack and the grid is complete without other changes to your script. Hopefully, this tweak solves your problems.

complete script with border hack and comment around the hack.:

<?php

// Start Sessie
//session_start();

// Map settings
$mapWidth		= 5;
$mapHeight		= 5;
$squareSize 	= 70;
$squareColor	= '#333';

// Content settings
$monster		= '&#129430;';
$treasure		= '&#127873;';
$start			= '&#128578;';
$exit			= '&#128274;';

// Make SVG files
$fontSize = $squareSize * 2;
$svg['center']	= '<rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />';
$svg['right']	= '<rect width="10%" height="20%" x="90%" y="40%" />';
$svg['bottom']	= '<rect width="20%" height="10%" x="40%" y="90%" />';
$svg['left']	= '<rect width="10%" height="20%" x="0" y="40%" />';
$svg['top']		= '<rect width="20%" height="10%" x="40%" y="0%" />';

// Function to count the values in a field
function count_values($arr)
{
	$counting = 0;
	foreach ($arr as $key => $val)
	{
		if (is_numeric($val))
		{
			$counting = $counting + $val;
		}
	}
	return $counting;
}

// Calculate Monsters
$monsters = ceil($mapHeight * $mapWidth * 0.16);

// Calculate Chests
$chests = floor($mapHeight * $mapWidth * 0.24);

// Check map size
if ($monsters == 0 || $chests == 0 || ($monsters + $chests + 2) > ($mapHeight * $mapWidth))
{
	print 'Please change your mapsize';
	exit;
}

// Create content
$squareChests	= array_fill(1, $chests, 'treasure');
$squareMonsters	= array_fill(1, $monsters, 'monster');
$squareStart	= array(1 => 'start');
$squareEnd		= array(1 => 'exit');

// Merge and mix all content
$fillbox = array_merge($squareChests, $squareMonsters, $squareStart, $squareEnd);
shuffle($fillbox);

/******************************\
	Start Grid Map
\******************************/


		
// Build column
for ($row=1; $row<=$mapHeight; $row++)
{
	// Build row
	for ($col=1; $col<=$mapWidth; $col++)
	{
		$randomSquare = array();
		while (count_values($randomSquare) < 1)
		{
		// Default settings
		$squareMaxTop		= 1;
		$squareMaxLeft		= 1;
		$squareMaxBottom	= 1;
		$squareMaxRight		= 1;

		// Top row has never a door at top
		if ($row == 1){ $squareMaxTop = 0; }
		// First column has never a door at left
		if ($col == 1){ $squareMaxLeft = 0; }
		// Last row has never a door at the bottom
		if ($row == $mapHeight){ $squareMaxBottom = 0; }
		// Last column has never a door at the right
		if ($col == $mapWidth){ $squareMaxRight = 0; }

			$randomSquare = array(
				'top'		=> rand(0, $squareMaxTop),
				'right'		=> rand(0, $squareMaxRight),
				'bottom'	=> rand(0, $squareMaxBottom),
				'left'		=> rand(0, $squareMaxLeft),
			);
		}
		$field[$row][$col] = $randomSquare;
	}
}

/******************************\
	Check each field = connected
\******************************/
foreach ($field as $row => $colArr)
{
	foreach ($colArr as $col => $door)
	{
		// Check top door is connected with previous top field
		$previousTop = $row - 1;
		$field[$previousTop][$col] = isset($field[$previousTop][$col]) ? $field[$previousTop][$col] : NULL;
		if ($field[$previousTop][$col] != NULL)
		{
			$field[$row][$col]['top'] = $field[$previousTop][$col]['bottom'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['top'] = 1;
				$field[$previousTop][$col]['bottom'] = 1;
			}
		} else {
			unset($field[$previousTop]);
		}

		// Check right door is connected with next right field
		$nextRight = $col + 1;
		$field[$row][$nextRight] = isset($field[$row][$nextRight]) ? $field[$row][$nextRight] : NULL;
		if ($field[$row][$nextRight] != NULL)
		{
			$field[$row][$col]['right'] = $field[$row][$nextRight]['left'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['right'] = 1;
				$field[$row][$nextRight]['left'] = 1;
			}
		} else {
			unset($field[$row][$nextRight]);
		}

		// Check bottom door is connected with next bottom field
		$nextBottom = $row + 1;
		$field[$nextBottom][$col] = isset($field[$nextBottom][$col]) ? $field[$nextBottom][$col] : NULL;
		if ($field[$nextBottom][$col] != NULL)
		{
			$field[$row][$col]['bottom'] = $field[$nextBottom][$col]['top'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['bottom'] = 1;
				$field[$nextBottom][$col]['top'] = 1;
			}
		} else {
			unset($field[$nextBottom]);
		}

		// Check left door is connected with previous left field
		$previousLeft = $col - 1;
		$field[$row][$previousLeft] = isset($field[$row][$previousLeft]) ? $field[$row][$previousLeft] : NULL;
		if ($field[$row][$previousLeft] != NULL)
		{
			$field[$row][$col]['left'] = $field[$row][$previousLeft]['right'];
			if (count_values($field[$row][$col]) < 1)
			{
				$field[$row][$col]['left'] = 1;
				$field[$row][$previousLeft]['right'] = 1;
			}
		} else {
			unset($field[$row][$previousLeft]);
		}

		// Give dead end always a monster / treasure / Start or Exit
		if (count_values($field[$row][$col]) == 1 && count($fillbox) > 0)
		{
			$field[$row][$col]['content'] = $fillbox[0];
			array_shift($fillbox);
		} else {
			$field[$row][$col]['content'] = '';
		}
	}
}

/**************************************************\
	Check broken paths
	If broken path, add entry to random field in broken path
\**************************************************/
// How to do ?????

/**************************************************\
	Check walking circles
	If circle, remove one entry from random field in cicle path
\**************************************************/
// How to do ?????

// Set leftover content to random fields
while (count($fillbox) > 0)
{
	$x = rand(1, $mapHeight);
	$y = rand(1, $mapWidth);
	if ($field[$x][$y]['content'] == '')
	{
		$field[$x][$y]['content'] = $fillbox[0];
		array_shift($fillbox);
	}
}

/******************************\
	Show grid
\******************************/
foreach ($field as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		// Create and show image SVG
		$image = '<svg width="'.$squareSize.'px" height="'.$squareSize.'px" fill="'.$squareColor.'" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL;
		$image .= '	'.$svg['center'].PHP_EOL; // Center square
		// The doors edit: border control happens here. so essentially, the broken paths are created here with your original code.
		if ($rowKey == 1) { $image .= '	'.PHP_EOL; } else { $image .= '	'.$svg['top'].PHP_EOL; }
		if ($colKey == 1) { $image .= '	'.PHP_EOL; } else { $image .= '	'.$svg['left'].PHP_EOL; }
		if ($rowKey == 5) { $image .= '	'.PHP_EOL; } else { $image .= '	'.$svg['bottom'].PHP_EOL; }
		if ($colKey == 5) { $image .= '	'.PHP_EOL; } else { $image .= '	'.$svg['right'].PHP_EOL; }
        // the above code closes the doors around the grid (borders) but does not break the other doors.
		// The text (content)
		$text = '';
		if ($colArr['content'] == 'monster'){ $text = $monster; }
		if ($colArr['content'] == 'treasure'){ $text = $treasure; }
		if ($colArr['content'] == 'start'){ $text = $start; }
		if ($colArr['content'] == 'exit'){ $text = $exit; }
		$image .= '	<text x="50%" y="50% "textLength="100%" font-size="'.$fontSize.'%" dominant-baseline="middle" text-anchor="middle">'.$text.'</text>'.PHP_EOL;
		$image .= '</svg>'; // Close SVG

		print $image; // Show image
	}
	print '<br>'; // Next row
}

?>

 

I thought that you wanted every square to be connected, which is what i changed. I looked at your code again and i see what you are doing: the smiley is the start and the lock is the exit. The walking in a circle is doors that should be broken but all columns should not have four doors. Still, i wouldn't program it this way but it's your code.

we get a better view of what is happening when we print the contents of your field array. Take a look at the array, if you have time. take a closer look at yourcheck each field = connected code.

echo '<br><br>';
foreach ($field as $row => $colArr)
{
	foreach ($colArr as $col => $door)
	{
		echo '<table cellspacing="2" cellpadding="2" border="1" width="400"><tr><td bgcolor="#f0f0f0" width="100">row</td><td bgcolor="#f0f0f0" width="100">col</td><td bgcolor="#f0f0f0" width="100">door</td><td bgcolor="#f0f0f0" width="100">content</td></tr>';
		foreach ($door as $direction => $content) {
		  //echo 'row: ' . $row . ', col: ' . $col . ', door: ' . $position . ', item: ' . $item . '<br>';
		  echo '<tr><td>' . $row . '</td><td>' . $col . '</td><td>' . $direction . '</td><td>' . $content . '</td></tr>';
		}
		echo '</table>';
	}
}
echo '<br><br>';

where is the text 'content' coming from in this array? i have yet to locate the source. meantime, row -1 is curious since that would be row 0 and there is not a row 0 (for row = 1).

I've changed the code a little and added a better output to view

The chance to have a broken path is lesser but still possible

<?php

// Grid settings
$gridWidth	= 3;
$gridHeight	= 4;
$svgSize	= 100;
$svgColor	= '#333';

// Images (Icons)
$iconExit		= '&#128274;'; // Shows a lock
$iconMonster	= '&#129430;'; // Shows a dinosaur
$iconStart		= '&#128578;'; // Shows a smiley
$iconTreasure	= '&#127873;'; // Shows a gift

// Calculate Monsters
$monsters = ceil($gridHeight * $gridWidth * 0.15);

// Calculate Treasures
$treasures = floor($gridHeight * $gridWidth * 0.25);

// Make iconBox and shuffleBox
$iconBox = array_merge(
	array_fill(1, $treasures, $iconTreasure),
	array(1 => $iconExit),
	array_fill(1, $monsters, $iconMonster),
	array(1 => $iconStart)
);
$shuffleBox = $iconBox;
shuffle($shuffleBox);

// Build the full grid with all information
$grid = array(
	'grid'			=> array_fill(1, $gridHeight, array_fill(1, $gridWidth, array(
		'binary'		=> '0000',
		'decimal'		=> 0,
		'doors'			=> array(
			'bottom'	=> 0,
			'left'		=> 0,
			'right'		=> 0,
			'top'		=> 0
	),
		'icon'			=> NULL,
		'svg'			=> NULL
	))),
	'icons'			=> array(
		'exit'			=> $iconExit,
		'monster'		=> $iconMonster,
		'treasure'		=> $iconTreasure,
		'start'			=> $iconStart
	),
	'iconBox'		=> array(
		'default'		=> $iconBox,
		'shuffle'		=> $shuffleBox
	),
	'information'	=> array(
		'emptyFields'	=> (($gridHeight * $gridWidth) - ($monsters + $treasures + 2)),
		'gridHeight'	=> $gridHeight,
		'gridWidth'		=> $gridWidth,
		'monsters'		=> $monsters,
		'svgColor'		=> $svgColor,
		'svgSize'		=> $svgSize,
		'treasures'		=> $treasures,
		'totalFields'	=> ($gridHeight * $gridWidth),
		'totalIcons'	=> count($iconBox)
	)
);

// Function to count the values in a field
function count_values($arr)
{
	$counting = 0;
	foreach ($arr as $key => $val)
	{
		if (is_numeric($val))
		{
			$counting = $counting + $val;
		}
	}
	return $counting;
}

// Build the fields
foreach ($grid['grid'] as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		// Default random settings
		$randomBottom	= 1;
		$randomLeft		= 1;
		$randomRight	= 1;
		$randomTop		= 1;

		// Last row has never a door at the bottom
		if ($rowKey == $grid['information']['gridHeight']){ $randomBottom = 0; }
		// First column has never a door at left
		if ($colKey == 1){ $randomLeft = 0; }
		// Last column has never a door at the right
		if ($colKey == $grid['information']['gridWidth']){ $randomRight = 0; }
		// Top row has never a door at top
		if ($rowKey == 1){ $randomTop = 0; }

		// Create random square with at least 1 door
		while (count_values($grid['grid'][$rowKey][$colKey]['doors']) < 1)
		{
			$grid['grid'][$rowKey][$colKey]['doors'] = array(
				'bottom'	=> rand(0, $randomBottom),
				'left'		=> rand(0, $randomLeft),
				'right'		=> rand(0, $randomRight),
				'top'		=> rand(0, $randomTop)
			);

			// Get previous top and left field
			$previousLeft	= $colKey - 1;
			$previousTop	= $rowKey - 1;

			// Check connection with top field
			if ($previousTop > 0)
			{
				if ($grid['grid'][$previousTop][$colKey]['doors']['bottom'] == 1)
				{
					$grid['grid'][$rowKey][$colKey]['doors']['top'] = 1;
				} else {
					$grid['grid'][$previousTop][$colKey]['doors']['bottom'] = $grid['grid'][$rowKey][$colKey]['doors']['top'];
				}
			}

			// Check connection with left field
			if ($previousLeft > 0)
			{
				if ($grid['grid'][$rowKey][$previousLeft]['doors']['right'] == 1)
				{
					$grid['grid'][$rowKey][$colKey]['doors']['left'] = 1;
				} else {
					$grid['grid'][$rowKey][$previousLeft]['doors']['right'] = $grid['grid'][$rowKey][$colKey]['doors']['left'];
				}
			}

			// Check broken path with top
			if ($previousTop > 0)
			{
				if (count_values($grid['grid'][$rowKey][$colKey]['doors']) == 1 && count_values($grid['grid'][$previousTop][$colKey]['doors']) == 1)
				{
					$grid['grid'][$rowKey][$colKey]['doors']['top'] = 0;
				}
			}

			// Check broken path with left
			if ($previousLeft > 0)
			{
				if (count_values($grid['grid'][$rowKey][$colKey]['doors']) == 1 && count_values($grid['grid'][$rowKey][$previousLeft]['doors']) == 1)
				{
					$grid['grid'][$rowKey][$colKey]['doors']['left'] = 0;
				}
			}

		}
	}
}

// Add some extra information
foreach ($grid['grid'] as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		// Make a binary
		$grid['grid'][$rowKey][$colKey]['binary'] = 
			$grid['grid'][$rowKey][$colKey]['doors']['top'].
			$grid['grid'][$rowKey][$colKey]['doors']['left'].
			$grid['grid'][$rowKey][$colKey]['doors']['bottom'].
			$grid['grid'][$rowKey][$colKey]['doors']['right'];

		// Convert binary to decimal
		$grid['grid'][$rowKey][$colKey]['decimal'] = bindec($grid['grid'][$rowKey][$colKey]['binary']);

		// Always fill dead ends with an icon
		if (count_values($grid['grid'][$rowKey][$colKey]['doors']) == 1 && count($shuffleBox) > 0)
		{
			// This add an icon to a dead end
			$grid['grid'][$rowKey][$colKey]['icon'] = $shuffleBox[0];
			array_shift($shuffleBox);
		}
	}
}

// Set leftover from shuffleBox to random fields
while (count($shuffleBox) > 0)
{
	$x = rand(1, $grid['information']['gridWidth']);
	$y = rand(1, $grid['information']['gridHeight']);
	if ($grid['grid'][$y][$x]['icon'] == NULL)
	{
		$grid['grid'][$y][$x]['icon'] = $shuffleBox[0];
		array_shift($shuffleBox);
	}
}

// Make SVG files
$svg['center']	= '<rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />'; // A simple square
$svg['right']	= '<rect width="10%" height="20%" x="90%" y="40%" />'; // Put a door (path) to the right side of the square
$svg['bottom']	= '<rect width="20%" height="10%" x="40%" y="90%" />'; // Put a door (path) to the bottom side of the square
$svg['left']	= '<rect width="10%" height="20%" x="0" y="40%" />'; // Put a door (path) to the left side of the square
$svg['top']		= '<rect width="20%" height="10%" x="40%" y="0%" />'; // Put a door (path) to the top side of the square

// Create SVG for each field
foreach ($grid['grid'] as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		$grid['grid'][$rowKey][$colKey]['svg'] = '<svg width="'.$grid['information']['svgSize'].'px" height="'.$grid['information']['svgSize'].'px" fill="'.$grid['information']['svgColor'].'" xmlns="http://www.w3.org/2000/svg">';
		$grid['grid'][$rowKey][$colKey]['svg'] .= $svg['center'];
		// The doors
		if ($grid['grid'][$rowKey][$colKey]['doors']['top']		== 1) { $grid['grid'][$rowKey][$colKey]['svg'] .= $svg['top']; }
		if ($grid['grid'][$rowKey][$colKey]['doors']['right']	== 1) { $grid['grid'][$rowKey][$colKey]['svg'] .= $svg['right']; }
		if ($grid['grid'][$rowKey][$colKey]['doors']['bottom']	== 1) { $grid['grid'][$rowKey][$colKey]['svg'] .= $svg['bottom']; }
		if ($grid['grid'][$rowKey][$colKey]['doors']['left']	== 1) { $grid['grid'][$rowKey][$colKey]['svg'] .= $svg['left']; }
		// Add icon
		if ($grid['grid'][$rowKey][$colKey]['icon'] != NULL)
		{
			$grid['grid'][$rowKey][$colKey]['svg'] .= '<text x="50%" y="50%" textLength="100%" font-size="'.($grid['information']['svgSize'] * 2).'%" dominant-baseline="middle" text-anchor="middle">'.$grid['grid'][$rowKey][$colKey]['icon'].'</text>';
		}
		// Close SVG
		$grid['grid'][$rowKey][$colKey]['svg'] .= '</svg>';
	}
}

// Make information block
$information = '';
foreach($grid['information'] as $key => $content)
{
	$information .= '	<tr>
		<td>'.$key.'</td>
		<td>'.$content.'</td>
	</tr>';
}

$icons = '';
foreach($grid['icons'] as $key => $content)
{
	$icons .= '	<tr>
		<td>'.$key.'</td>
		<td>'.$content.'</td>
	</tr>'.PHP_EOL;
}

// Create image and table
$image = '';
$table = '';
foreach ($grid['grid'] as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		$image .= $colArr['svg'].PHP_EOL;
		$table .= '		<tr>
			<td>'.$rowKey.'</td>
			<td>'.$colKey.'</td>
			<td>'.$colArr['doors']['right'].'</td>
			<td>'.$colArr['doors']['bottom'].'</td>
			<td>'.$colArr['doors']['left'].'</td>
			<td>'.$colArr['doors']['top'].'</td>
			<td>'.$colArr['icon'].'</td>
			<td>'.$colArr['binary'].'</td>
			<td>'.$colArr['decimal'].'</td>
			<td>'.$colArr['svg'].'</td>
		</tr>'.PHP_EOL;
	}
}

/**************************************************\
	Just a simple HTML + CSS for a better view
	Don't look at the source code, it's not valid
	It's made very quick
\**************************************************/
print '<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Random Grid Map</title>
<style type="text/css">
html
{
	background: none #fff;
	display: block;
	height: 100%;
	margin: 0 0 0 0;
	overflow: hidden;
	padding: 0 0 0 0;
	position: relative;
	width: 100%;
}

body
{
	display: grid;
	font-family: "Verdana";
	font-size: 1em;
	font-weight: 100;
	grid-column-gap: 0;
	grid-row-gap: 0;
	grid-template-areas:
		"header header"
		"svg information"
		"svg table"
		"footer footer";
	grid-template-columns: 1fr 1fr;
	grid-template-rows: auto auto 1fr auto;
	height: 100%;
	margin: 0 0 0 0;
	overflow: hidden;
	padding: 0 0 0 0;
	width: 100%;
}

h1
{
	border-bottom: solid 1px #000;
	font-size: 3em;
	grid-area: header;
	margin: 0 0 0.5em 0;
	padding: 0 0 0.2em 0;
	text-align: center;
}

div.rgm
{
	background: none #369;
	display: inline-block;
	grid-area: svg;
	margin: 0 auto 0 auto;
	overflow: auto;
	padding: 0.5em 0.5em 0.5em 0.5em;
}

div.rgm svg
{
	background: none transparent;
	float: left;
	margin: 0 0 0 0;
	padding: 0 0 0 0;
}

div.rgm svg:nth-child('.$grid['information']['gridWidth'].'n+1)
{
	clear: left;
}

div.information
{
	grid-area: information;
	overflow: auto;
}

div.information table
{
	border: solid 1px #000;
	border-spacing: 0;
	border-collapse: separate;
	float:left;
	margin: 0 1em 0 1em;
}

div.information table tr td
{
	margin: 0 0 0 0;
	padding: 0.2em 0.2em 0.2em 0.2em;
	vertical-align: middle;
}

div.information table tr td[colspan="2"]
{
	font-weight: 900;
	text-align: center;
}

div.information table tr:nth-child(odd) td
{
	background-color: #fff;
}

div.information table tr:nth-child(even) td
{
	background-color: #ddd;
}

div.table
{
	grid-area: table;
	margin: 1em 0 0 0;
	overflow: auto;
}

div.table table
{
	border: none 0 transparent;
	border-spacing: 0;
	border-collapse: separate;
}

div.table table thead tr th
{
	background: none #fff;
	border-bottom: solid 1px #000;
	margin: 0 0 0 0;
	padding: 0.2em 0.3em 0.2em 0.3em;
	position: sticky;
	top: 0;
}

div.table table tbody td
{
	margin: 0 0 0 0;
	padding: 0.5em 0 0.5em 0;
	text-align: center;
}

footer
{
	border-top: solid 1px #000;
	grid-area: footer;
	margin: 0 0 0 0;
	padding: 0 0 0 0;
}

footer p
{
	margin: 0 0 0 0;
	padding: 0.5em 0 0.5em 0;
	text-align: center;
}
</style>
</head>

<body>
<h1>Random Grid Map</h1>
<div class="rgm">
'.$image.'</div>
<div class="information">
<table>
	<tr>
		<td colspan="2">Information</td>
	</tr>
'.$information.'
</table>
<table>
	<tr>
		<td colspan="2">Icons</td>
	</tr>
'.$icons.'
</table>
</div>
<div class="table">
<table>
	<thead>
		<tr>
			<th>Row</th>
			<th>Column</th>
			<th>Right Door</th>
			<th>Bottom Door</th>
			<th>Left Door</th>
			<th>Top Door</th>
			<th>Icon</th>
			<th>Binary</th>
			<th>Decimal</th>
			<th>SVG</th>
		</tr>
	</thead>
	<tbody>
'.$table.'	</tbody>
</table>
</div>
<footer>
	<p><a href="'.$_SERVER['PHP_SELF'].'">Click here to refresh the page and make a new Random Grid Map</a> or press "F5" on your keyboard</p>
</footer>
</body>
</html>';

?>

 

I wonder why you do not want to build a grid and cut a path at the same time as well as build an array to track the grid. You could easily create the grid and a grid array in one fell swoop.

<?php

// Grid settings
$gridWidth	= 5;
$gridHeight	= 5;
$svgSize	= 100;
$svgColor	= '#333';
$svg['center']	= '<rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />';
// Images (Icons)
$iconExit		= '&#128274;'; // Shows a lock
$iconMonster	= '&#129430;'; // Shows a dinosaur
$iconStart		= '&#128578;'; // Shows a smiley
$iconTreasure	= '&#127873;'; // Shows a gift

// Calculate Monsters
$monsters = ceil($gridHeight * $gridWidth * 0.15);

// Calculate Treasures
$treasures = floor($gridHeight * $gridWidth * 0.25);

// Make iconBox and shuffleBox
$iconBox = array_merge(
	array_fill(1, $treasures, $iconTreasure),
	array(1 => $iconExit),
	array_fill(1, $monsters, $iconMonster),
	array(1 => $iconStart)
);
$shuffleBox = $iconBox;
shuffle($shuffleBox);

// Build the full grid with all information
$grid = array();
for ($i = 1; $i <= $gridHeight; $i++) {
    for ($j = 1; $j <= $gridWidth; $j++) {
		$grid[$i][$j] = 'content';
		$image = '<svg width="'.$svgSize.'px" height="'.$svgSize.'px" fill="'.$svgColor.'" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL;
		$image .= '	'.$svg['center'].PHP_EOL; // Center square
	   	$image .= '</svg>'; // Close SVG
		echo $image; // Show image
	}
    echo '<br>';
}
//echo '<br>'; print_r($grid);
//echo '<br><br>';
$k = 1; 
foreach ($grid as $row => $association) {
	$l = 1;
	foreach ($association as $column => $data) {
	    echo 'row ' . $k . ', column ' . $l . ': ' . $data . '<br>';
	    $l++;
    } $k++; echo '<br>';
}

?>

your code seems to contradict the doors because the doors are not being used in anyway other than creating paths around a random maze. I think that this is somewhat like the old Atari game Haunted House (one of my favs.) Anyway, you could write a make path algorithm and build the doors from there, which would now only be bottom or right. I think that you are overthinking. Even better is to use mathematics to design the grid and path but it is not necessary.

I'm trying to build this program myself in spare time but i out of time for now. I have added the "doors" and documented them in the array.

<?php

// Grid settings
$gridWidth	= 3;
$gridHeight	= 4;
$svgSize	= 100;

// Images (Icons)
$iconExit		= '&#128274;'; // Shows a lock
$iconMonster	= '&#129430;'; // Shows a dinosaur
$iconStart		= '&#128578;'; // Shows a smiley
$iconTreasure	= '&#127873;'; // Shows a gift

// Calculate Monsters
$monsters = ceil($gridHeight * $gridWidth * 0.15);

// Calculate Treasures
$treasures = floor($gridHeight * $gridWidth * 0.25);

// Make iconBox and shuffleBox
$iconBox = array_merge(
	array_fill(1, $treasures, $iconTreasure),
	array(1 => $iconExit),
	array_fill(1, $monsters, $iconMonster),
	array(1 => $iconStart)
);
$shuffleBox = $iconBox;
shuffle($shuffleBox);

/* Build the full grid with all information ///////////////////////////////////////////////*/
$grid = array();

for ($i = 1; $i <= $gridHeight; $i++) {
    for ($j = 1; $j <= $gridWidth; $j++) {
        $grid[$i][$j]['top'] = $grid[$i][$j]['left'] = $grid[$i][$j]['bottom'] = $grid[$i][$j]['right'] = 0;
	$image = '<svg width="'.$svgSize.'px" height="'.$svgSize.'px" fill="#333" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL;
	$image .= '<rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />'.PHP_EOL; // Center square
	if ($i > 1 && $i <= $gridHeight) { /* top */ $grid[$i][$j]['top'] = 1; $image .= '	' . '<rect width="20%" height="10%" x="40%" y="0%" />'.PHP_EOL; }
	if ($j > 1 && $j <= $gridWidth) { /* left */ $grid[$i][$j]['left'] = 1; $image .= '	' . '<rect width="10%" height="20%" x="0%" y="40%" />'.PHP_EOL; }
	if ($i < $gridHeight) { /* bottom */ $grid[$i][$j]['bottom'] = 1; $image .= '	' . '<rect width="20%" height="100%" x="40%" y="90%" />'.PHP_EOL; }
	if ($j < $gridWidth) { /* right */ $grid[$i][$j]['right'] = 1; $image .= '	' . '<rect width="10%" height="20%" x="90%" y="40%" />'.PHP_EOL; }
	$image .= '</svg>';
	echo $image;
    }
    echo '<br>';
}
//echo '<br>'; print_r($grid);
//echo '<br><br>';
$k = 1; 
foreach ($grid as $row => $association) {
    $l = 1;
    foreach ($association as $column => $connection) {
        foreach ($connection as $door => $status) {
	    echo 'row ' . $k . ', column ' . $l . ', ' . $door . ' door: ' . $status . '<br>';
        }
        $l++;
    }
    $k++; echo '<br>';
}

?>

Now you just need to think of a path algorithm which needs documented coordinates. Once you have a path, that doesn't involve randomly disabling doors which require checking for broken paths, you can then loop over the necessary connections to disable them as well as display the surprise items.

I've removed a few unnecessary variables and comments from the code. I'll try to wok on a path algorithm along side of your path algorithm ideas and we'll see if we can solve your problems. Unless, someone else beats us to it...


I did some research last night and i stumbled across grid path algorithms in connection with grid games, which could help you determine a path. Mathematicians have created these algorithms so there is no need for us to re-invent the wheel.

http://qiao.github.io/PathFinding.js/visual/

so you must decide if you want to use a path algorithm or if you want to use the tile numbers (re-invent the wheel) to calculate with basic arithmetic (neighbors would require more calculation to avoid misplacing traps and blocking a valid path).

i've set up the grid with random start and goal nodes which can be passed to an algorithm (which requires adding this code to the grid build and display code)

<?php

// Grid settings
$maxColumns    = (int) 3;
$maxRows    = (int) 4;
if ($maxColumns < 3 || $maxColumns > 10 || $maxRows < 3 || $maxRows > 10) {
    //screen width must be a factor
    print 'Please change your mapsize';
    exit;
}
// Images (Icons)
$iconMonster    = '&#129430;'; // Shows a dinosaur
$iconTreasure    = '&#127873;'; // Shows a gift
$monsters = ceil($maxRows * $maxColumns * 0.15);
$treasures = floor($maxRows * $maxColumns * 0.25);
$svgSize    = 100;
$fontSize = $svgSize * 2;

// Build the full grid with all information
$grid = array();
$displayGridArray = (int) 1;
$paths = array();
$trapSize = ceil(($maxColumns * $maxRows) / $maxRows - 1);
$availableTiles = $maxColumns * $maxRows - ($trapSize + 2);

//create random start and goal nodes
$nodes = array();
$nodes['start']['icon'] = '&#128578';
$nodes['start']['row'] = mt_rand(1, $maxRows);
$nodes['start']['column'] = mt_rand(1, $maxColumns);
$nodes['goal']['icon'] = '&#128274';
$nodes['goal']['row'] =  mt_rand(1, $maxRows);
$nodes['goal']['column'] = mt_rand(1, $maxColumns);
while ($nodes['goal']['column'] === $nodes['start']['column']) {
    $nodes['goal']['column'] = mt_rand(1, $maxColumns);
}

//build and display the grid
$tileCounter = 0;
for ($i = 1; $i <= $maxRows; $i++) {
    for ($j = 1; $j <= $maxColumns; $j++) {
        $tileCounter++;
        $grid[$i][$j]['tile'] = $tileCounter;
        $grid[$i][$j]['exits'] = null;
        $image = '<svg width="'.$svgSize.'px" height="'.$svgSize.'px" fill="#333" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL;
        $image .= '<title>tile: '.$grid[$i][$j]['tile'].'&#xA;row: '.$i.'&#xA;column: '.$j.'</title><rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />'.PHP_EOL; // Center square
        if ($i === $nodes['start']['row'] && $j === $nodes['start']['column']) {
            $image .= '<text x="50%" y="50% "textLength="100%" font-size="'.$fontSize.'%" dominant-baseline="middle" text-anchor="middle">'.$nodes['start']['icon'].'</text>'.PHP_EOL;
        }
        if ($i === $nodes['goal']['row'] && $j === $nodes['goal']['column']) {
            $image .= '<text x="50%" y="50% "textLength="100%" font-size="'.$fontSize.'%" dominant-baseline="middle" text-anchor="middle">'.$nodes['goal']['icon'].'</text>'.PHP_EOL;
        }
        if ($i > 1 && $i <= $maxRows) { /* north,top */ $grid[$i][$j]['exits'] .= 'north'; $image .= '<rect width="20%" height="10%" x="40%" y="0%" />'.PHP_EOL; }
        if ($j > 1 && $j <= $maxColumns) { /* west,left */ $grid[$i][$j]['exits'] .= (!empty($grid[$i][$j]['exits']) ? ', ' : '') . 'west'; $image .= '<rect width="10%" height="20%" x="0%" y="40%" />'.PHP_EOL; }
        if ($i < $maxRows) { /* south,bottom */ $grid[$i][$j]['exits'] .= (!empty($grid[$i][$j]['exits']) ? ', ' : '') . 'south'; $image .= '<rect width="20%" height="100%" x="40%" y="90%" />'.PHP_EOL; }
        if ($j < $maxColumns) { /* east,right */ $grid[$i][$j]['exits'] .= (!empty($grid[$i][$j]['exits']) ? ', ' : '') . 'east'; $image .= '<rect width="10%" height="20%" x="90%" y="40%" />'.PHP_EOL; }
           $image .= '</svg>';
        echo $image;
    }   echo '<br>'.PHP_EOL;
}

if ($displayGridArray) {
echo '<p>';
$k = 1; 
foreach ($grid as $row => $association) {
    $l = 1;
    foreach ($association as $column => $data) {
        echo 'row ' . $k . ', column ' . $l;
        foreach ($data as $label => $info) {
        echo ', ' . $label . ': ' . $info;
        }  echo '<br>';
        $l++;
    }
    $k++;
}
echo '</p>';
}
echo 'total tiles: ' . $tileCounter . '<br>';
echo 'start node: ' . $nodes['start']['row'] . ':' . $nodes['start']['column'] . ' tile ' . $grid[$nodes['start']['row']][$nodes['start']['column']]['tile'] . '<br>';
echo 'goal node: ' . $nodes['goal']['row'] . ':' . $nodes['goal']['column'] . ' tile ' . $grid[$nodes['goal']['row']][$nodes['goal']['column']]['tile'] . '<br>';
echo 'traps: ' . $trapSize . '<br>';
echo 'available tiles = ' . $availableTiles . ' (minus traps, start and goal nodes)<br>';

?>

now you just have to place the monsters and treasures into columns not belonging to start and goal nodes, calculate a path or possible paths, place traps not interfering with atleast one valid path from start to goal and remove the exits that create the traps..

i also created a variable for displaying the grid array for development purposes.

let us know if you have problems finishing the code.
 

I fixed the "Broken Path" source
New in this update:
You can set the setWrong to true or false
When false it will build a random grid, when true it will show a selfmade wrong grid to show better where to fix the broken paths. If you refresh the page you will see the doors will be random added when a path is broken.

<?php

// Start a new session
session_start();

/**************************************************\
	Settings
\**************************************************/
// Grid settings
$gridWidth	= 6;
$gridHeight	= 7;
$svgSize	= 50;
$svgColor	= '#333';
$setWrong	= true;

/**************************************************\
	Functions
\**************************************************/
// Function to count the values in a field
function count_values($arr)
{
	$counting = 0;
	foreach ($arr as $key => $val)
	{
		if (is_numeric($val))
		{
			$counting = $counting + $val;
		}
	}
	return $counting;
}

// Function to show the grid on screen
function viewGrid($thisGrid = array(), $thisSize = 50)
{
// SVG
	$svg = array(
		'center'	=> '<rect width="80%" height="80%" x="10%" y="10%" rx="5%" ry="5%" />',
		'top'		=> '<rect width="20%" height="10%" x="40%" y="0%" />',
		'right'		=> '<rect width="10%" height="20%" x="90%" y="40%" />',
		'bottom'	=> '<rect width="20%" height="100%" x="40%" y="90%" />',
		'left'		=> '<rect width="10%" height="20%" x="0%" y="40%" />',
		'start'		=> '<text x="50%" y="50% "textLength="100%" font-size="'.($thisSize * 2).'%" dominant-baseline="middle" text-anchor="middle">&#128578;</text>',
		'exit'		=> '<text x="50%" y="50% "textLength="100%" font-size="'.($thisSize * 2).'%" dominant-baseline="middle" text-anchor="middle">&#128274;</text>',
		'monster'	=> '<text x="50%" y="50% "textLength="100%" font-size="'.($thisSize * 2).'%" dominant-baseline="middle" text-anchor="middle">&#129430;</text>',
		'treasure'	=> '<text x="50%" y="50% "textLength="100%" font-size="'.($thisSize * 2).'%" dominant-baseline="middle" text-anchor="middle">&#127873;</text>',
	);

	foreach ($thisGrid as $rowKey => $rowArr)
	{
		foreach ($rowArr as $colKey => $colArr)
		{
			$thisColor = $colArr['color'];
			// Create and show image SVG
			$image = '<svg width="'.$thisSize.'px" height="'.$thisSize.'px" fill="'.$thisColor.'" xmlns="http://www.w3.org/2000/svg">';
			$image .= $svg['center']; // Center square
			// The doors
			if ($colArr['doors']['top']		== 1) { $image .= $svg['top']; }
			if ($colArr['doors']['right']	== 1) { $image .= $svg['right']; }
			if ($colArr['doors']['bottom']	== 1) { $image .= $svg['bottom']; }
			if ($colArr['doors']['left']	== 1) { $image .= $svg['left']; }
			if ($thisGrid[$rowKey][$colKey]['content'] != NULL)
			{
				$image .= $svg[$thisGrid[$rowKey][$colKey]['content']];
			}
			$image .= '</svg>'; // Close SVG

			print $image; // Show image
		}
		print '<br>'.PHP_EOL; // Next row
	}
}

/**************************************************\
	Grid building
\**************************************************/
// Build an empty grid
$grid = array_fill(1, $gridHeight, array_fill(1, $gridWidth, array(
	'doors'			=> array(
		'bottom'	=> 0,
		'left'		=> 0,
		'right'		=> 0,
		'top'		=> 0
	),
	'color'			=> $svgColor,
	'content'		=> NULL,
	'check'			=> NULL
)));

// Fill empty grid with random fields
foreach ($grid as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		// Default random doors
		$randomTop = 1;
		$randomRight = 1;
		$randomBottom = 1;
		$randomLeft = 1;

		// Borders can never have a door
		if ($rowKey == 1) { $randomTop = 0; }
		if ($rowKey == $gridHeight) { $randomBottom = 0; }
		if ($colKey == 1) { $randomLeft = 0; }
		if ($colKey == $gridWidth) { $randomRight = 0; }

		// Set random doors
		while (count_values($grid[$rowKey][$colKey]['doors']) == 0)
		{
			$grid[$rowKey][$colKey]['doors']['top'] = rand(0, $randomTop);
			$grid[$rowKey][$colKey]['doors']['right'] = rand(0, $randomRight);
			$grid[$rowKey][$colKey]['doors']['bottom'] = rand(0, $randomBottom);
			$grid[$rowKey][$colKey]['doors']['left'] = rand(0, $randomLeft);
		}
	}
}

// Get a wrong grid
if ($setWrong == true)
{
	unset($grid);

	// Create a broken grid to check
	$wrongGrid[1][1]['doors'] = array(
		'top' => 0,
		'right' => 0,
		'bottom' => 1,
		'left' => 0,
	);

	$wrongGrid[1][2]['doors'] = array(
		'top' => 0,
		'right' => 1,
		'bottom' => 0,
		'left' => 0,
	);

	$wrongGrid[1][3]['doors'] = array(
		'top' => 0,
		'right' => 0,
		'bottom' => 0,
		'left' => 1,
	);

	$wrongGrid[2][1]['doors'] = array(
		'top' => 0,
		'right' => 1,
		'bottom' => 0,
		'left' => 0,
	);

	$wrongGrid[2][2]['doors'] = array(
		'top' => 0,
		'right' => 0,
		'bottom' => 0,
		'left' => 1,
	);

	$wrongGrid[2][3]['doors'] = array(
		'top' => 0,
		'right' => 0,
		'bottom' => 1,
		'left' => 0,
	);

	$wrongGrid[3][1]['doors'] = array(
		'top' => 0,
		'right' => 1,
		'bottom' => 0,
		'left' => 0,
	);

	$wrongGrid[3][2]['doors'] = array(
		'top' => 0,
		'right' => 0,
		'bottom' => 0,
		'left' => 1,
	);

	$wrongGrid[3][3]['doors'] = array(
		'top' => 1,
		'right' => 0,
		'bottom' => 0,
		'left' => 0,
	);

	// Set new width en height
	$gridHeight	= count($wrongGrid);
	$gridWidth	= count($wrongGrid[$gridHeight]);

	foreach ($wrongGrid as $rowKey => $rowArr)
	{
		foreach ($rowArr as $colKey => $colArr)
		{
			$grid[$rowKey][$colKey]['doors'] = $colArr['doors'];
			$grid[$rowKey][$colKey]['color'] = '#300';
			$grid[$rowKey][$colKey]['content'] = NULL;
			$grid[$rowKey][$colKey]['check'] = NULL;
		}
	}
	print 'Fill Grid with wrong grid<br>';
} else {
	print 'Fill Grid with random doors<br>';
}

viewGrid($grid, $svgSize);

// Connect all doors
foreach ($grid as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		if ($grid[$rowKey][$colKey]['doors']['top'] == 1)
		{
			$grid[$rowKey][$colKey]['doors']['top'] = 1;
			$grid[($rowKey - 1)][$colKey]['doors']['bottom'] = 1;
		}

		if ($grid[$rowKey][$colKey]['doors']['right'] == 1)
		{
			$grid[$rowKey][$colKey]['doors']['right'] = 1;
			$grid[$rowKey][($colKey + 1)]['doors']['left'] = 1;
		}

		if ($grid[$rowKey][$colKey]['doors']['bottom'] == 1)
		{
			$grid[$rowKey][$colKey]['doors']['bottom'] = 1;
			$grid[($rowKey + 1)][$colKey]['doors']['top'] = 1;
		}

		if ($grid[$rowKey][$colKey]['doors']['left'] == 1)
		{
			$grid[$rowKey][$colKey]['doors']['left'] = 1;
			$grid[$rowKey][($colKey - 1)]['doors']['right'] = 1;
		}
	}
}
print 'Show grid with all connected doors<br>';
viewGrid($grid, $svgSize);

// Check broken path
foreach ($grid as $rowKey => $rowArr)
{
	foreach ($rowArr as $colKey => $colArr)
	{
		// Set Keys
		$oldRow = $rowKey;
		$oldCol = $colKey;
		$newRow = $rowKey;
		$newCol = $colKey;
		// Set check grid
		$checkGrid = $grid;
		// Fall back by dead end
		$fallBack = array();
		// Start walking
		if ($colKey == $gridWidth && $rowKey == $gridHeight)
		{
			$grid[$rowKey][$colKey]['check'] = 1;
			$keepGoing = false;
		} elseif ($grid[$rowKey][$colKey]['check'] == 1)
		{
			$keepGoing = false;
		} else {
			$grid[$rowKey][$colKey]['check'] = '1';
			$keepGoing = true;
			// Remember walking
			$rememberWalking = array();
		}
		// Start check
		while ($keepGoing === true)
		{
			// Remember Walking Path
			$addPath = array(
				'row' => $newRow,
				'col' => $newCol
			);
			$rememberWalking[count($rememberWalking)] = $addPath;
			// Set Fall Back
			if (count_values($checkGrid[$newRow][$newCol]['doors']) > 1)
			{
				$rememberPosition = array(
					'row' => $oldRow,
					'col' => $oldCol
				);
				$fallBack[] = $rememberPosition;
			}

			// Go to next field
			if ($checkGrid[$newRow][$newCol]['doors']['right'] == 1)
			{
				$newCol++;
				$checkGrid[$oldRow][$oldCol]['doors']['right'] = 0;
				$checkGrid[$newRow][$newCol]['doors']['left'] = 0;
			} elseif ($checkGrid[$newRow][$newCol]['doors']['bottom'] == 1)
			{
				$newRow++;
				$checkGrid[$oldRow][$oldCol]['doors']['bottom'] = 0;
				$checkGrid[$newRow][$newCol]['doors']['top'] = 0;
			} elseif ($checkGrid[$newRow][$newCol]['doors']['left'] == 1)
			{
				$newCol--;
				$checkGrid[$oldRow][$oldCol]['doors']['left'] = 0;
				$checkGrid[$newRow][$newCol]['doors']['right'] = 0;
			} elseif ($checkGrid[$newRow][$newCol]['doors']['top'] == 1)
			{
				$newRow--;
				$checkGrid[$oldRow][$oldCol]['doors']['top'] = 0;
				$checkGrid[$newRow][$newCol]['doors']['bottom'] = 0;
			} elseif (count($fallBack) > 0)
			{
				$lastPosition = end($fallBack);
				array_pop($fallBack);
				$newRow = $lastPosition['row'];
				$newCol = $lastPosition['col'];
				$grid[$newRow][$newCol]['check'] = 0;
			} else {
				// Broken Path Found
				$lastRow = 0;
				$lastCol = 0;
				foreach ($rememberWalking as $color)
				{
					if ($color['row'] > $lastRow)
					{
						$lastRow = $color['row'];
						$lastCol = $color['col'];
					} elseif ($color['col'] > $lastCol)
					{
						$lastCol = $color['col'];
					}
				}
				$directions = array('top', 'right', 'bottom', 'left');
				if ($lastRow == 1 || $grid[$lastRow][$lastCol]['doors']['top'] == 1)
				{
					$key = array_search('top', $directions);
					unset($directions[$key]);
				}
				if ($lastCol == 1 || $grid[$lastRow][$lastCol]['doors']['left'] == 1)
				{
					$key = array_search('left', $directions);
					unset($directions[$key]);
				}
				if ($lastRow == $gridHeight || $grid[$lastRow][$lastCol]['doors']['bottom'] == 1)
				{
					$key = array_search('bottom', $directions);
					unset($directions[$key]);
				}
				if ($lastCol == $gridWidth || $grid[$lastRow][$lastCol]['doors']['right'] == 1)
				{
					$key = array_search('right', $directions);
					unset($directions[$key]);
				}
				shuffle($directions);
				$grid[$lastRow][$lastCol]['doors'][$directions[0]] = 1;
				$checkGrid[$lastRow][$lastCol]['doors'][$directions[0]] = 1;
				if ($directions[0] == 'top')
				{
					$grid[($lastRow - 1)][$lastCol]['doors']['bottom'] = 1;
					$checkGrid[($lastRow - 1)][$lastCol]['doors']['bottom'] = 1;
				} elseif ($directions[0] == 'bottom')
				{
					$grid[($lastRow + 1)][$lastCol]['doors']['top'] = 1;
					$checkGrid[($lastRow + 1)][$lastCol]['doors']['top'] = 1;
				} elseif ($directions[0] == 'left')
				{
					$grid[$lastRow][($lastCol - 1)]['doors']['right'] = 1;
					$checkGrid[$lastRow][($lastCol - 1)]['doors']['right'] = 1;
				} elseif ($directions[0] == 'right')
				{
					$grid[$lastRow][($lastCol + 1)]['doors']['left'] = 1;
					$checkGrid[$lastRow][($lastCol + 1)]['doors']['left'] = 1;
				}
				$grid[$newRow][$newCol]['check'] = 0;
				$newCol = $lastCol;
				$newRow = $lastRow;
				$grid[$newRow][$newCol]['check'] = 0;
			}





			if (($newCol == $gridWidth && $newRow == $gridHeight))
			{
				// Remember Walking Path
				$addPath = array(
					'row' => $newRow,
					'col' => $newCol
				);
				unset($rollBack);
				$keepGoing = false;
			} else {
				$grid[$newRow][$newCol]['check'] = 1;
				$oldCol = $newCol;
				$oldRow = $newRow;
			}

		}
	}
}

print 'Show new grid without broken paths.<br>';
viewGrid($grid);

?>

 

Hi Elvium, The new code script works better than your original script in the original post (no broken paths and no 'walking in circles') but the code often produces the same results for several page loads (not very random). I reloaded the page 5 times and the grid didn't change.

You could calculate borders and neighbors very easily and use a function to walk around the grid in search of problems and code solutions to any problems.

I have detected borders and neighbors since my last post and even added diagonal neighbors in case the grid would be changed to allow diagonal movements. I've added a function to return opposite directions (north returns south). I've also added a Manhattan distance function and it works well.

I wonder what you are building: a game, a path finding exercise or something else?

ultimately classes would be better for this project. Imagine calling new grid(), new trap(), new monster() etc. Not necessary in development mode, of course, but a better idea in production.

Anyway, if you are happy with your new code, then i suppose that this thread is closed. Otherwise, let us know if you need further assistance. I am going to keep working on my own grid code because i find my grid handling code useful in many ways. Plus, i am intrigued by the mathematics behind path finding. Very fascinating.
 

19 hours ago, jodunno said:

Hi Elvium, The new code script works better than your original script in the original post (no broken paths and no 'walking in circles') but the code often produces the same results for several page loads (not very random). I reloaded the page 5 times and the grid didn't change.

You could calculate borders and neighbors very easily and use a function to walk around the grid in search of problems and code solutions to any problems.

I have detected borders and neighbors since my last post and even added diagonal neighbors in case the grid would be changed to allow diagonal movements. I've added a function to return opposite directions (north returns south). I've also added a Manhattan distance function and it works well.

I wonder what you are building: a game, a path finding exercise or something else?

ultimately classes would be better for this project. Imagine calling new grid(), new trap(), new monster() etc. Not necessary in development mode, of course, but a better idea in production.

Anyway, if you are happy with your new code, then i suppose that this thread is closed. Otherwise, let us know if you need further assistance. I am going to keep working on my own grid code because i find my grid handling code useful in many ways. Plus, i am intrigued by the mathematics behind path finding. Very fascinating.
 

Turn the $setWrong to false for having a random grid.

The walking in circles is still an error but I think I have a solution in my mind.

When all the errors are fixed I will put it in a class

38 minutes ago, Elvium said:

Turn the $setWrong to false for having a random grid.

The walking in circles is still an error but I think I have a solution in my mind.

When all the errors are fixed I will put it in a class

Hi Elvium, so now you think that I'm stupid. LOL. I've done that but my point is that you have a predictable method of disabling 'doors'. When i set the variable to false, the grid reverts to a full grid after a certain amount of clicks on top of predictability (which simply moves left-to-right in many sequences, thus defying randomness.)

see my attached image of the false setting and the full grid. (cityblocks1 .jpg)

The mathematical algorithms mentioned early can help solve your problems. We do not have a true grid here and it is not weighted with vertices, so many algorithms will not function well. However, it is a cross between a city block map with streets (doors) and a maze. Which is why the manhattan distance (taxi cab) and A* algorithms will work to help with the maze like obstacles of your dead-ends. You don't have to think of anything new, just convert algorithms to php and output data that fits your code (a bit more tricky).

I only have so much time per day to work on my grid code. Today i started calculating a central axis and center tiles . I've also added corner tiles- vide cityblocks2.jpg attachment. I'm preparing to integrate A* algorithm but i want to read a bit more about maze generating algorithms for a better trapping/obstacle layout. I'm not a math guru so it am a bit slow.

Anyway, i'm just trying to help you succeed in the best possible way. Predictability is not good when we are talking randomness. rand is not a truly random algorithm, which leaves us with mathematical methods.

 

cityblocks1.jpg

cityblocks2.jpg

Hi Elvium,

I've been busy lately. My Son starts school again and i'm back to sleeping 5-6 hours  per day during the week.

Anyway, I have finally completed a center finding algorithm and it is working well. I decided to focus on the svg. Now i have never studied svg, so i have alot to learn about designing images using svg code. I think that your concept of doors is problematic when you handle them as top left bottom and right stubs that are connected with each svg output to the browser. Multiple svg images is also problematic because if one shrinks the browser window then the tiles are no longer connected. I've been wanting to create a single svg with only bottom and right doors. I've finished the code days ago but i've been working on implementing it with a php loop. Calculating the coordinates is not too difficult. However, i made an svg smiley instead of text and that was difficult to calculate. I have spent the last two days of free time building it and calculating it's position.

The following code is simply html with the single svg method as an example of what mean with top bottom left and right causing problems. I do not see that the 'doors' are being used in any way other than perception. So why not switch to bottom and right, which will be easier to enable and disable in code.

single svg

<html>
<head>
  <title></title>
</head>
<body>

<svg width="270" height="180" xmlns="http://www.w3.org/2000/svg">
  <rect width="80" height="80" x="0" y="10" rx="10" ry="10" fill="#333" />
  <rect width="10" height="20" x="80" y="42" rx="0" ry="0" fill="#444" stroke="#333" stroke-width="4" />
  <rect width="80" height="80" x="90" y="10" rx="10" ry="10" fill="#333" />
  <rect width="10" height="20" x="170" y="42" rx="0" ry="0" fill="#444" stroke="#333" stroke-width="4" />
  <rect width="80" height="80" x="180" y="10" rx="10" ry="10" fill="#333" />

  <rect width="20" height="10" x="30" y="90" rx="0" ry="0" fill="#444" stroke="#333" stroke-width="4" />
  <rect width="10" height="20" x="80" y="130" rx="0" ry="0" fill="#444" stroke="#333" stroke-width="4" />
  <rect width="80" height="80" x="0" y="100" rx="10" ry="10" fill="#333" />
  <rect width="20" height="10" x="120" y="90" rx="0" ry="0" fill="#444" stroke="#333" stroke-width="4" />
  <rect width="10" height="20" x="170" y="130" rx="0" ry="0" fill="#444" stroke="#333" stroke-width="4" />

  <rect width="80" height="80" x="90" y="100" rx="10" ry="10" fill="#333" />
  <rect width="20" height="10" x="210" y="90" rx="0" ry="0" fill="#444" stroke="#333" stroke-width="4" />
  <rect width="80" height="80" x="180" y="100" rx="10" ry="10" fill="#333" />
</svg>


</body>
</html>

again, i have programmed his single svg into php because coordinates and dimensions need to be dynamically calculated.

2 hours ago, jodunno said:

Multiple svg images is also problematic because if one shrinks the browser window then the tiles are no longer connected.

The advantage of SVG (Scalable Vector Graphics) is that, as the name implies, they are scalable. Resizing should not separate the elements.

Take this example svg

<svg width='100%' viewBox='0 0 100 100' >
    <circle cx='30' cy='30' r='25' fill='#333'/>
    <circle cx='70' cy='70' r='25' fill='#333'/>
    <line x1='30' y1='30' x2='70' y2='70' stroke='#333' stroke-width='8'/>
</svg>

The viewBox defines the source coordinate system, the width defines the output size.  If I output the same image into two differently sized containers, the image expands to fit.

EG

<?php
    
    $svg = "<svg width='100%' viewBox='0 0 100 100' >
                <circle cx='30' cy='30' r='25' fill='#333'/>
                <circle cx='70' cy='70' r='25' fill='#333'/>
                <line x1='30' y1='30' x2='70' y2='70' stroke='#333' stroke-width='8'/>
            </svg>
           ";
?>

<!DOCTYPE html>                              
<html lang='en'>
<meta charset='utf-8'>
<head>
<title>Example</title>
</head>
<body>
    
    <div style='width:20%'>
        <?=$svg?>
    </div>
    
        
    <div style='width:50%'>
        <?=$svg?>
    </div>
</body>
</html>

image.png.5288d2a1adc9dee3671af17215c05e34.png

You just need to to create all your connected cells inside a single SVG image.

I have been having a go at this grid problem myself and now have version which has just created 500 unique  6x5 grid maps with no circular routes and no isolated groups of cells. Each time you try to connect two cells it first verifies that it will not result in a circular path. It also verifies that all cells are reachable from any other cell. The paths through the maps are saved to a database and the path is defined as UNIQUE to ensure no repeats are saved. My grid table is like this...

image.thumb.png.f0438863f3da637ee5f9b9fc38c36fe0.png

Code...

<?php
    require 'db_inc.php';
    $pdo = mdbConnect('db1');
    
    
$invalid = 0;

$num_maps_required = 500;        // if 1 is required it is displayed. If multiple they as saved to database
$gridWidth  = 6; 
$gridHeight = 5;


for ($g=0; $g<$num_maps_required; $g++)   {

    ################################################################################
    #  BUILD THE GRID SQUARES
    ################################################################################
    // Grid settings
    $svgSize    = 100;
    $svgColor   = '#333';

    // Images (Icons)
    $iconExit     = '&#128274;'; // Shows a lock
    $iconMonster  = '&#129430;'; // Shows a dinosaur
    $iconStart    = '&#128578;'; // Shows a smiley
    $iconTreasure = '&#127873;'; // Shows a gift

    // Calculate Monsters
    $monsters = ceil($gridHeight * $gridWidth * 0.15);

    // Calculate Treasures
    $treasures = floor($gridHeight * $gridWidth * 0.25);

    // Make iconBox and shuffle
    $iconBox = array_merge(
        array_fill(0, $treasures, $iconTreasure),
        array($iconExit),
        array_fill(0, $monsters, $iconMonster),
        array($iconStart),
        array_fill(0, $gridHeight * $gridWidth - $monsters - $treasures - 2, '')
    );
    shuffle($iconBox);

    $cells = [];
    foreach ($iconBox as $k => $icn)  {
        $r = intdiv($k, $gridWidth);
        $c = $k % $gridWidth;
        $poss = [ $k-$gridWidth, $k-1, $k+1, $k+$gridWidth ];
        if ($r == 0) unset($poss[0]);
        if ($r == $gridHeight - 1) unset($poss[3]);
        if ($c == 0) unset($poss[1]);
        if ($c == $gridWidth - 1) unset($poss[2]);           
        $cnx = array_fill_keys($poss, 0);
        $cells[] = [ 'icon' => $icn,
                     'cnxs' => $cnx,
                     'reachable' => []
                   ];
    }

    ################################################################################
    #  RANDOMLY CREATE A LINK BETWEEN EACH PAIR OF ADJACENT COLUMNs AND 
    #  ONE OR TWO LINKS BETWEEN EACH PAIR OF ADJACENT ROWS.
    ################################################################################
        $rows = range(0, $gridHeight-1);
        $cols = range(0, $gridWidth-2);
        shuffle($rows);
        foreach ($cols as $c)  {
            $r = array_pop($rows);
            $s = $r * $gridWidth + $c;
            $e = $s + 1;
            connectCells($s, $e, $cells);
        }

        $rows = range(0, $gridHeight-2);
        $cols = range(0, $gridWidth-1);
        shuffle($cols);
        foreach ($rows as $r)  {
            for ($i=0; $i<rand(1,2); $i++)
            {
            $c = array_pop($cols);
            $s = $r * $gridWidth + $c;
            $e = $s + $gridWidth;
            connectCells($s, $e, $cells);
            }
        }

    ################################################################################
    #  FIND THE UNCONNECTED CELLS AND CONNECT THEM TO THEIR NEIGHBOURS
    #    --  NOTE coonectCells() CHECKS NO CIRCULAR PATH IS CREATED
    ################################################################################

        #  $loneCells = array_filter($cells, fn($v)=>empty(array_filter($v['cnxs'])));
        $loneCells = array_filter($cells, fn($v)=>count(array_filter($v['cnxs'])) < 2 );
        uasort($loneCells, fn($a, $b)=>rand(0,1)? -1: 1);
        foreach ($loneCells as $start => $lc)  {
            foreach ($lc['cnxs'] as $end => $v)  {
                if (!$v )  {
                    connectCells($start, $end, $cells);
                }
            }
        }


    ################################################################################
    #  MAP NOW GENERATED
    #  - VERIFY
    #  - SAVE THE ROUTES THROUGH THE MAZE FOR SUBSEQUENT ANALYSIS
    #  - PRINT THE MAP
    ################################################################################

        $cells[0]['reachable'] = [];
        findReachable(0, $cells, $cells[0]['reachable']);
        $pathLength = count($cells[0]['reachable']);
        $path = join(', ', $cells[0]['reachable']);
        if ($pathLength != $gridHeight * $gridWidth) {
            ++$invalid;
            echo "<h1>INVALID GRID MAP $invalid</h1>" ;
            echo "$pathLength cells: $path<br>";
        }
        else switch ( $num_maps_required )  {
                case 1:  echo printMap($gridHeight, $gridWidth, $cells);
                         break;    
                default: saveMap($pdo, $gridHeight, $gridWidth, $path, $pathLength, $cells);
                         break;
             }

}

################################################################################
#   FUNCTIONS
################################################################################

    function saveMap(PDO $pdo, $gridHeight, $gridWidth, $path, $pathLength, &$cells)
    {
        $pdo->beginTransaction();
        try {
            $stmtg = $pdo->prepare("INSERT IGNORE INTO grid (height, width, path, path_length) VALUES (?, ?, ?, ?)");
            $stmtgc = $pdo->prepare("INSERT INTO grid_cell (grid_id, cell_no, icon) VALUES (?, ?, ?)");
            $stmtcnx = $pdo->prepare("INSERT INTO cell_cnx (grid_id, from_cell, to_cell) VALUES (?, ?, ?)");
                                               
            $stmtg->execute( [ $gridHeight, $gridWidth, $path, $pathLength ] );
            $grid_id = $pdo->lastInsertId();                                                                                                    #TODO grid_cell table, cell_cnx table
            
            foreach ($cells as $cno => $cl)  {
                $stmtgc->execute( [ $grid_id, $cno, $cl['icon'] ] );
                
                foreach ($cl['cnxs'] as $k => $v)  {
                    if ($v && $k > $cno)  {
                        $stmtcnx->execute( [ $grid_id, $cno, $k ] );
                    }
                }
            }
            
            $pdo->commit();
        }
        catch(PDOException $e) {
            $pdo->rollBack();
            throw $e;
        }
    }

    /**
    * Recursively searches all possible paths through the maze from start 
    * and stores new reachable cells in array
    * 
    * @param int $from   starting cell
    * @param array pointer $cells  cells array
    * @param array pointer $reachable  all reachable cells
    */
    function findReachable($from, &$cells, &$reachable)
    {
        foreach ($cells[$from]['cnxs'] as $k => $v)  {
            if ($v) {
                if (!in_array($k, $reachable))  {
                    $reachable[] = $k;
                    findReachable($k, $cells, $reachable);
                }
            }
        }
    }


    function connectCells($start, $end, &$cells)
    {
        $cells[$end]['reachable'] = [];
        findReachable($end, $cells, $cells[$end]['reachable']);
        #
        # if cell is already reachable then connecting to it would result in a circular path
        #
        if (in_array($start, $cells[$end]['reachable'])) {
            return 0;
        }
        $cells[$start]['cnxs'][$end] = 1;   
        $cells[$end]['cnxs'][$start] = 1;
        return 1;   
    }            

    function printMap ($rows, $cols, &$cells)
    {
        $cellSize = 80;
        $cellBgd  = '#333';
        $wid = $cols * $cellSize;
        $ht  = $rows * $cellSize;
        $ix = 10;
        $iy = 10;
        $cx = $cy = $cellSize/2;
        $isz = $cellSize - 20;
        $ty = 47;
        $ty2 = $cellSize - 14;
        
        $svg = "<svg width='$wid'  viewBox='0 0 $wid $ht'>
                <rect x='0' y='0' width='$wid' height='$ht' fill='linen'  />
                ";
        foreach ($cells as $k => $cell) {
            $transx = $k % $cols * $cellSize;
            $transy = intdiv($k, $cols) * $cellSize;
            $sColor = count(array_filter($cell['cnxs']))==1 ? '#aaa' : $cellBgd;
            $svg .= "<g transform='translate($transx, $transy)' >
                    <rect x='$ix' y='$iy' width='$isz' height='$isz' rx='8' ry='8' fill='$cellBgd' stroke='$sColor' stroke-width='2'/>
                    ";
            foreach ($cell['cnxs'] as $c => $connected)  {
                if ($c > $k && $connected)
                    switch ($c - $k)  {
                        case 1:      $svg .= "<path d='M $cx $cy h $cellSize' stroke='$cellBgd' stroke-width='10' />\n"; break;
                        case $cols:  $svg .= "<path d='M $cx $cy v $cellSize' stroke='$cellBgd' stroke-width='10' />\n"; break;
                    }
            }      
            $svg .= "<text x='$cx' y='$ty' font-size='24px' fill='#FFF' text-anchor='middle'>{$cell['icon']}</text>
                     <text x='$cx' y='$ty2' font-size='12px' fill='#FFF' text-anchor='middle'>$k</text>
                    </g>
                    ";
        }
                    
        $svg .= "</svg>\n";
        return $svg;
    }
?>

The next step is to be able to select a stored grid map from the DB and display it.
 

On 9/3/2024 at 11:16 PM, Barand said:

The advantage of SVG (Scalable Vector Graphics) is that, as the name implies, they are scalable. Resizing should not separate the elements.

have you looked at the code provided by Elvium? I am aware of the definition of scalable but the individual disconnected tiles in his code are breaking like table cells in Firefox 1. My suggestion is to place the rectangles in a single svg, which is what you have done in your grid code. However, i also recommend creating only two possible doors: bottom and right.

Your code is very nice, as usual. Great job. I do not care for the database storage but it is well implemented. You are a master coder. I'm still using my free time to finish my grid but my son is back in school now. I have a little less time in the day. Also, today i had some time but i was leaving my flat and i stumbled across an old man with his pants pulled down and saw a pee puddle on the floor. I was pretty mad. Minus details, he said he was in my building to visit the attorney upstairs. I said, in German language,  "German is not my native language and i can read the sign on the front door that the attorney's office is closed". I added "Das ist nicht in Ordnung." What a crappy day. Who enters a building and urinates on the floor? The bad part about the story is that a different attorney was contacted by Police (instead of Police coming when i called them to come). The attorney seemed strangely nonchalant about the matter. I exploded and threw some expletives her way and warned her against continuing to talk in my direction. Needless to say, my free time is much less today. What is wrong with people nowadays?

Anyway, I am storing intelligence about the grid in arrays, which can be used in JavaScript if it is to be a game (neighbors, borders, corner tiles, median tiles, distance calculations, etc).

Elvium should appreciate your code. Nice work!

3 hours ago, jodunno said:

However, i also recommend creating only two possible doors: bottom and right.

I agree those are sufficient to define and draw the cell connections but, to be able to trace the paths through the network, it's a lot easier to know that A connects to B and also that B connects to A - that requires the top and left ones too.

The database gave me an easy way to check for uniqueness and that I wasn't just recreating the same grid over and over.

  • Like 1
1 hour ago, Barand said:

I agree those are sufficient to define and draw the cell connections but, to be able to trace the paths through the network, it's a lot easier to know that A connects to B and also that B connects to A - that requires the top and left ones too.

The database gave me an easy way to check for uniqueness and that I wasn't just recreating the same grid over and over.

yes, i know what you mean and it is a complicated network. I decided to scan neighbors while building a grid array so that i know which connections can be made. This data will be useful in path determinations, especially with a second array holding broken connections awaiting validation.

But your code is splendid and works around the problem differently. I will try to get my code done soon. The weekend is coming. Woohoo 🙂

  • 3 weeks later...

Finally, i got around to completing my cycle breaking code, which circumvents a path finding algorithm already provided by Barand. My code is raw development code so don't fall out of your chairs while viewing the mess. LOL.

<?php
/*/////////////////////////////////////////////////////////////////////////////////////////////////*/
/*// WARNING! raw and rough development code lies ahead.                                         //*/
/*// m = row, n = column, mp np = mn parity, p = perimiter, t = tile number,                     //*/
/*// N = north, W = west, S = south, E = east and  subsequently NW NE SW SE                      //*/
/*// cv = central vertices, cy = cycles, cr = crossroads or intersections,                       //*/
/*// cyan = cycle analysis, q = quadratic or square matrix, a = matrix entry ai,j                //*/
/*// omidirectional paths N,W,S,E = crossroads (four-ways or "circles")                          //*/
/*// development information:                                                                    //*/
/*// made in Germany                                                                             //*/
/*// my vlc repeat playlist: Sabrina Carpenter - Espresso and Glorilla - Yeah, Glo!              //*/
/*// author: Sherkosky, John                                                                     //*/
/*/////////////////////////////////////////////////////////////////////////////////////////////////*/
$ac = array (); /* accounting array. you can count on it! */
$ac['cr'] = $ac['cv'] = $ac['q'] = $ac['n-1'] = $ac['n+1'] = $ac['m-1'] = $ac['m+1'] = $ac['np'] = $ac['mp'] = $ac['v'] = $ac['n'] = $ac['m'] = (int) 0;
$cyan = $ac['p'] = $ac['e'] = $cv = array (); $cyan['cy'] = array (); $cyan['cr'] = array ();
$ac['e']['vn'] = $ac['e']['hm'] = $ac['e']['s'] = $ac['e']['z'] = $ac['e']['y'] = $ac['e']['x'] = (int) 0;
$algor = array(); $algor['cb'] = 1; $algor['pf'] = 0;

/* obviously, post inclusion is development code only. validation and restrictions withheld
   in addition to this fact, and most obvious in code, the form has since been removed. */
empty($_POST['m']) ? $ac['m'] = (int) 3: $ac['m'] = (int) $_POST['m'];
empty($_POST['n']) ? $ac['n'] = (int) 3: $ac['n'] = (int) $_POST['n'];
$ac['v'] = $ac['m'] * $ac['n'];
if ($ac['m'] === $ac['n']) { $ac['q'] = 1; } /* quadratic or square matrix */

$p = array();
$p['c'] = $p['E'] = $p['S'] = $p['W'] = $p['N'] = $p['u'] = array();

/* initialize grid graph arrays. be smart. */
$init_gg = function(int $ab = 0, int $cd = 0, int $i = 0, int $m = 0, int $n = 0, int $v = 0) {
  $gg = array();
  switch ($ab) {
    case 0: /* row and column index for tiles (an address book or whois function) */
        for ($j = 1; $j <= $m; $j++) {
          for ($k = 1; $k <= $n; $k++) {
            $gg[$j][$k]['t'] = $i; $i++;
        }} break;
    case 1: /* (0) initialize main array organized by tile numbers $t. (1) cardinal directions. (2) ordinal directions. */
      for ($i; $i <= $v; $i++) {
        switch ($cd) {
          case 0: $gg[$i] = Array('xy' => Array('m' => 0, 'n' => 0, 'a' => 0, 'mn' => '', 'dp' => $i % 2 !== 0 ? 0: 1), 'a*' => Array('g' => 0, 'h' => 0, 'f' => 0 )); break;
          case 1: $gg[$i]['N'] = 0; $gg[$i]['W'] = 0; $gg[$i]['S'] = 0; $gg[$i]['E'] = 0; break;
          case 2: $gg[$i]['NW'] = 0; $gg[$i]['NE'] = 0; $gg[$i]['SW'] = 0; $gg[$i]['SE'] = 0; break;
      }} break;
  } return $gg;
};
$gg = (array) $init_gg(0, 0, 1, $ac['m'], $ac['n'], $ac['v']);
$ggt = (array) $init_gg(1, 0, 1, 0, 0, $ac['v']);
$ggcd = (array) $init_gg(1, 1, 1, 0, 0, $ac['v']);
$ggod = (array) $init_gg(1, 2, 1, 0, 0, $ac['v']);
$gge = array (); /* gg edges: hm = horizontal m or x. vn = vertical n or y */
$gge['hm'] = array (); $gge['vn'] = array ();
$gge['c'] = (int) 0; /* debugging: count of cycle disconnections */
$ggDisplay = (int) 1; /* development variable for viewing gg data */
$nwse = $k = $w = $e = $a = $r = $t = $j = $i = (int) 0;

$nodes = array();
$nodes['s']['n'] = $nodes['s']['m'] = $nodes['s']['t'] = (int) 0;
$nodes['g']['n'] = $nodes['g']['m'] = $nodes['g']['t'] = (int) 0;
$nodes['s']['i'] = $nodes['s']['mn'] = (string) '';
$nodes['g']['i'] = $nodes['g']['mn'] = (string) '';
$nodes['n']['d'] = $nodes['m']['d'] = null;

/* svg tiles. later to be if (svg), since i am building a canvas version */
$svg = array();
$svg['mx'] = $svg['sy'] = $svg['sx'] = $svg['ry'] = $svg['rx'] = (int) 0;
$svg['rf'] = (string) 'bg101010';
$svg['ry'] = $svg['rx'] = 10;
$svg['e'] = array();
$svg['e']['vy'] = $svg['e']['vx'] = $svg['e']['hy'] = $svg['e']['hx'] = (int) 0;
$svg['e']['hx'] = 80; $svg['e']['hy'] = 90;
$svg['e']['vx'] = 30; $svg['e']['vy'] = 40;

/* ////////////////////////////////////////////////////////////////////////////////////////////////*/
/* functions                                                                                       */
/* ////////////////////////////////////////////////////////////////////////////////////////////////*/
function calculate_center($m, $n, $v, $mparity, $nparity) {
  /* mt = medial tiles, d = direction, t = tiles */
  $mt = (array) ['d' => 'center', 't' => (array) []];
  $half = floor(($v + 1) / 2); $halves = $half + 1;
  $t1 = $half - floor($half / $m); $t2 = $t1 + 1; $t3 = $t1 + $n; $t4 = $t3 + 1;
  switch ($nparity) {
    case 0:
      if ($mparity) { $mt['d'] ='vertical'; $mt['t'][$t1] = $t1; $mt['t'][$t3] = $t3; break; }
      $mt['t'][$half] = $half; break;
    case 1:
      if ($mparity) { $mt['t'][$t1] = $t1; $mt['t'][$t2] = $t2; $mt['t'][$t3] = $t3; $mt['t'][$t4] = $t4; break; }
      $mt['d'] ='horizontal'; $mt['t'][$half] = $half; $mt['t'][$halves] = $halves; break;
  } return (array) $mt;
}
function manhattan_distance(int $x1 = 0, int $x2 = 0, int $y1 = 0, int $y2 = 0) {
  return abs($x1-$x2) + abs($y1-$y2); /* h(n) = |xn - xg| + |yn - yg| */
}
function perimeters(int $m = 0, int $n = 0, int $v = 0, string $d = '', int $u = 0) {
  /* u=unique = faster and easier than using array_unique(array_merge()) on cardinal points */
  /* several methods of calculation exist. we picked one and loop. a loop is not necessary. */
  $perimeters = (array) [];
  switch ($u) {
    case 0:
      switch (strtoupper($d)) {
        case 'N': for ($i = 1; $i <= $n; $i++) { $perimeters[$i] = $i; } break;
        case 'W': for ($i = 1; $i <= $v; $i+=$n) { $perimeters[$i] = $i; } break;
        case 'S': for ($i = $v - ($n - 1); $i <= $v; $i++) { $perimeters[$i] = $i; } break;
        case 'E': for ($i = $n; $i <= $v; $i+=$n) { $perimeters[$i] = $i; } break;
      } break;
    case 1:
      for ($i = 1; $i <= $n; $i++) { array_push($perimeters, $i); }
      for ($i = 1+$n; $i < $v-($n - 1); $i+=$n) { array_push($perimeters, $i); }
      for ($i = $v - ($n - 1); $i <= $v; $i++) { array_push($perimeters, $i); }
      for ($i = $n*2; $i < $v; $i+=$n) { array_push($perimeters, $i); }
      sort($perimeters); break;
  } return $perimeters;
}
function randomize(int $min = 0, int $max = 0, string $fn = '') {
  /* allows user to select from multiple methods */
  $valid = (array) ['random_int', 'mt_rand', 'rand'];
  if (!in_array($fn, $valid) === true) { return false; }
  return $fn($min, $max);
}
function cbr(array $cy = [], array $e = [], callable $randomize){
  $i = (int) 1; $edges = array (); $re = (int) 0;
  $hve = array (); $hve['t'] = $hve['d'] = 0;
  foreach ($cy as $t => $s) { $edges[$i] = $t; $i++; }
  $re = array_rand($edges, 1);
  $hve['t'] = $edges[$re];
  switch ($re) { case 1: $re = 0; break; case 2: $re = 2; break; case 3: $re = 1; break; }
  switch ($re) {
    case 0:
      if (empty($e['hm'][$hve['t']]) && empty($e['vn'][$hve['t']])) {
        switch (randomize(1, 2, 'random_int')) {
          case 1: $hve['d'] = 'hm'; break(2);
          case 2: $hve['d'] = 'vn'; break(2);
        }
      } empty($e['hm'][$hve['t']]) ? $hve['d'] = 'hm': $hve['d'] = 'vn';
    break;
    case 1: $hve['d'] = 'hm'; break;
    case 2: $hve['d'] = 'vn'; break;
  } return $hve;
}

/*/////////////////////////////////////////////////////////////////////////////////////////////////*/
/*| initialize variables                                                                          |*/
/*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
$ac['mp'] = $ac['m'] % 2 !== 0 ? 0: 1;
$ac['np'] = $ac['n'] % 2 !== 0 ? 0: 1;
$ac['m+1'] = $ac['m']+1; $ac['n+1'] = $ac['n']+1;
$ac['m-1'] = $ac['m']-1; $ac['n-1'] = $ac['n']-1;
$cv = calculate_center($ac['m'], $ac['n'], $ac['v'], $ac['mp'], $ac['np']);
$ac['cv'] = count($cv['t']); /* central vertices */

/* edges = (m-1)n + (n-1)m = 2mn - m - n */
$ac['e']['x'] = ($ac['n']-1) * $ac['m'];
$ac['e']['y'] = ($ac['m']-1) * $ac['n'];
$ac['e']['s'] = $ac['e']['x'] + $ac['e']['y'];
$ac['e']['z'] = ($ac['m']-1) * ($ac['n']-1);
if ($ac['q']) { $ac['e']['z'] = pow($ac['n']-1, 2); }
$rv = array (); /* random vertex edge selections */

/* s = start, g = goal, m = row, n = column, i = icon, d = difference */
$nodes['s']['m'] = randomize(1, $ac['m'], 'random_int');
$nodes['s']['n'] = randomize(1, $ac['n'], 'random_int');
$nodes['s']['mn'] = $nodes['s']['m'].','.$nodes['s']['n'];
$nodes['s']['i'] = '&#128578';
$nodes['g']['i'] = '&#128274';
$nodes['g']['m'] = randomize(1, $ac['m'], 'random_int');
$nodes['g']['n'] = randomize(1, $ac['n'], 'random_int');
$nodes['g']['mn'] = $nodes['g']['m'].','.$nodes['g']['n'];
if ($nodes['g']['m'] === $nodes['s']['m']) {
  /* removing the if branch forces separate columns regardless of m values */
  while ($nodes['g']['n'] === $nodes['s']['n']) { $nodes['g']['n'] = randomize(1, $ac['n'], 'random_int'); }
}
$nodes['m']['d'] = abs($nodes['s']['m'] - $nodes['g']['m']);
$nodes['n']['d'] = abs($nodes['s']['n'] - $nodes['g']['n']);

/* perimeters array defining borders for mn neighbor calculations and other routines */
$p['N'] = perimeters($ac['m'], $ac['n'], $ac['v'], 'N', 0); $ac['p']['N'] = (int) count($p['N']);
$p['W'] = perimeters($ac['m'], $ac['n'], $ac['v'], 'W', 0); $ac['p']['W'] = (int) count($p['W']);
$p['S'] = perimeters($ac['m'], $ac['n'], $ac['v'], 'S', 0); $ac['p']['S'] = (int) count($p['S']);
$p['E'] = perimeters($ac['m'], $ac['n'], $ac['v'], 'E', 0); $ac['p']['E'] = (int) count($p['E']);
/* unique perimeters containing all cardinal points */
$p['u'] = perimeters($ac['m'], $ac['n'], $ac['v'], '', 1); $ac['p']['u'] = (int) count($p['u']);
/* corner (c) vertices of perimeters */
$p['c'] = (array) [
  'NW' => $p['N'][array_key_first($p['N'])], 'NE' => $p['N'][array_key_last($p['N'])],
  'SW' => $p['S'][array_key_first($p['S'])], 'SE' => $p['S'][array_key_last($p['S'])]
];

/* ////////////////////////////////////////////////////////////////////////////////////////////////*/
/* define the grid graph, vertex cost, vertex neighbors, cycles and edges                          */
/* ////////////////////////////////////////////////////////////////////////////////////////////////*/
for ($i = 1; $i <= $ac['m']; $i++) {
  for ($j = 1; $j <= $ac['n']; $j++) {
      $t++; $nwse = 0;
      $ggt[$t]['xy']['m'] = $i; $ggt[$t]['xy']['n'] = $j;
      $ggt[$t]['xy']['a'] = $i.$j; $ggt[$t]['xy']['mn'] = $i.','.$j;
      /* pre-calculate f(n) = g(n) + h(n) */
      $ggt[$t]['a*']['g'] = manhattan_distance($i, $nodes['s']['m'], $j, $nodes['s']['n']);
      $ggt[$t]['a*']['h'] = manhattan_distance($i, $nodes['g']['m'], $j, $nodes['g']['n']);
      $ggt[$t]['a*']['f'] = $ggt[$t]['a*']['g'] + $ggt[$t]['a*']['h'];
      /* calculate neighbors utilizing perimiter checks */
      if (empty($p['N'][$t])) { $ggcd[$t]['N'] = $t - $ac['n']; $nwse++; }
      if (empty($p['W'][$t]) && empty($p['N'][$t])) { $ggod[$t]['NW'] = $t - $ac['n+1']; }
      if (empty($p['E'][$t]) && empty($p['N'][$t])) { $ggod[$t]['NE'] = $t - $ac['n-1']; }
      if (empty($p['W'][$t])) { $ggcd[$t]['W'] = $t - 1; $nwse++; }
      if (empty($p['S'][$t])) { $ggcd[$t]['S'] = $t + $ac['n']; $nwse++; }
      if (empty($p['W'][$t]) && empty($p['S'][$t])) { $ggod[$t]['SW'] = $t + $ac['n-1']; }
      if (empty($p['E'][$t]) && empty($p['S'][$t])) { $ggod[$t]['SE'] = $t + $ac['n+1']; }
      if (empty($p['E'][$t])) { $ggcd[$t]['E'] = $t + 1; $nwse++; }
      if ($nwse === 4) { $cyan['cr'][$t] = $t; } /* crossroads or 4 degree vertices */
      /* South East Method: E border = v edges only and S border = h edges only. */
      if ($j < $ac['n']) { $gge['hm'][$t] = 0; }
      if ($i < $ac['m']) { $gge['vn'][$t] = 0; }
      if ($j < $ac['n'] && $i < $ac['m']) {
        if ($i < $ac['m-1']) { $cyan['cy'][$i][$j] = (array) [$t => 0, $t+1 => 0]; continue; }
        $cyan['cy'][$i][$j] = (array) [$t => 0, $t+1 => 0, $t+$ac['n'] => 0];
      } /* mn cycles */
}}
if (!empty($cyan['cr'])) { $ac['cr'] = count($cyan['cr']); } /* count crossroads. 2x2 is empty */
$ac['e']['hm'] = count($gge['hm']); $ac['e']['vn'] = count($gge['vn']);
$nodes['s']['t'] = $gg[$nodes['s']['m']][$nodes['s']['n']]['t'];
$nodes['g']['t'] = $gg[$nodes['g']['m']][$nodes['g']['n']]['t'];

if ($algor['cb']) { /* cycle breaker */
  for ($i = 1; $i <= $ac['m-1']; $i++) {
    for ($j = 1; $j <= $ac['n-1']; $j++) {
      $rv = cbr($cyan['cy'][$i][$j], $gge, 'randomize');
      $gge[$rv['d']][$rv['t']] = 1; $gge['c']++;
  }}
}

/* ////////////////////////////////////////////////////////////////////////////////////////////////*/
/* large plate of svg spaghetti with meatballs code                                                */
/* ////////////////////////////////////////////////////////////////////////////////////////////////*/
echo '<svg width="'.($ac['n']*90).'" height="'.($ac['m']*90+10).'" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL; // style="background-color: rgb(0,0,0,0.1);"
echo '<style>.edge { fill: #96ade9; fill-opacity: 0.5; stroke-width: 2; stroke: #96ade9; } .bg101010 { fill: #f0f0f0; fill-opacity: 0.8; stroke-width: 2px; stroke: #96ade9; } .bg808080 { fill: #808080; fill-opacity: 0.8; } .bg00a000 { fill: #00a000; fill-opacity: 0.8; } </style>'.PHP_EOL;
for ($i = 1; $i <= $ac['m']; $i++) {
  $svg['rx'] = 0; $svg['e']['hx'] = 80; $svg['e']['vx'] = 30;
  for ($j = 1; $j <= $ac['n']; $j++) {
      if ($gg[$i][$j]['t'] === $nodes['s']['t']) { $svg['sx'] = $svg['rx']+40; $svg['sy'] = $svg['ry']+40; $svg['mx'] = ($svg['sx']-8); }
      if (!empty($path[$gg[$i][$j]['t']])) { $svg['rf'] = 'bg00a000'; }
        echo "<rect width='80' height='80' x='".$svg['rx']."' y='".$svg['ry']."' rx='10' ry='10' class='".$svg['rf']."'><title>Tile " . $gg[$i][$j]['t'] ." (" . $i . ", " . $j . ")</title></rect>".PHP_EOL;
      if ($i.$j === $nodes['g']['m'].$nodes['g']['n']) {
        echo "<text x='".($svg['rx'] + 40)."' y='".($svg['ry'] + 40)."' textLength='100%' font-size='28' dominant-baseline='middle' text-anchor='middle'>".$nodes['g']['i']."</text>".PHP_EOL;
      }
      if ($j < $ac['n'] && empty($gge['hm'][$gg[$i][$j]['t']])) { echo "<rect width='10' height='20' x='".$svg['e']['hx']."' y='".$svg['e']['vy']."' rx='0' ry='0' class='edge' />".PHP_EOL; }
      if ($i < $ac['m'] && empty($gge['vn'][$gg[$i][$j]['t']])) { echo "<rect width='20' height='10' x='".$svg['e']['vx']."' y='".$svg['e']['hy']."' rx='0' ry='0' class='edge' />".PHP_EOL; }
      $svg['rx']+= 90; $svg['e']['hx']+= 90; $svg['e']['vx']+= 90; $svg['rf'] = 'bg101010';
  } $svg['ry']+= 90; $svg['e']['hy']+= 90; $svg['e']['vy']+= 90;
}
echo '<defs>'.PHP_EOL;
echo '  <g id="start_node">'.PHP_EOL;
echo '    <style>'.PHP_EOL;
echo '      .smiley_face { fill: #ffcc33; stroke: #666633; stroke-width: 2; }'.PHP_EOL;
echo '      .smiley_eyes { fill: #000000; stroke: #333333; stroke-width: 1; }'.PHP_EOL;
echo '      .smiley_eyes_white { fill: #f0f0f0; }'.PHP_EOL;
echo '    </style>'.PHP_EOL;
echo '    <title>Tile ' . $nodes['s']['t'] . ' (' . $nodes['s']['m'] . ', ' . $nodes['s']['n'] . ')</title>'.PHP_EOL;
echo '    <circle r="16" cx="'.$svg['sx'].'" cy="'.$svg['sy'].'" class="smiley_face" />'.PHP_EOL;
echo '    <circle r="3" cx="'. ($svg['sx'] -5) .'" cy="'. ($svg['sy'] -3) .'" class="smiley_eyes" />'.PHP_EOL;
echo '    <circle r="1" cx="'. ($svg['sx'] -4) .'" cy="'. ($svg['sy'] -4) .'" class="smiley_eyes_white" />'.PHP_EOL;
echo '    <circle r="3" cx="'. ($svg['sx'] + 6) .'" cy="'. ($svg['sy'] -3) .'" class="smiley_eyes" />'.PHP_EOL;
echo '    <circle r="1" cx="'. ($svg['sx'] + 7) .'" cy="'. ($svg['sy'] -4) .'" class="smiley_eyes_white" />'.PHP_EOL;
echo '    <path d="M'.$svg['mx'].','.($svg['sy']+7).' Q'.($svg['mx']+8).','.($svg['sy']+14).' '.($svg['mx']+16).','.($svg['sy']+7).'" fill="none" stroke="#000000" stroke-width="2" />'.PHP_EOL;
echo '  </g>'.PHP_EOL;
echo '</defs>'.PHP_EOL;
echo '<use href="#start_node" />'.PHP_EOL;
echo '</svg>'.PHP_EOL;
echo '<br>'.PHP_EOL;

echo '<p>start tile ' . $nodes['s']['t'] . ' = m' . $nodes['s']['m'] . ', n' . $nodes['s']['n'] . ' (g = 0)<br>';
echo 'goal tile ' . $nodes['g']['t'] . ' = m' . $nodes['g']['m'] . ', n' . $nodes['g']['n'] . ' (h = 0)</p>';
echo 'edges: ' . $ac['e']['s'] . ' (' . $ac['e']['x'] . ' hm, ' . $ac['e']['y'] . ' vn)<br>';
echo 'cycles: ' . $ac['e']['z'] . ' (' . $ac['m-1'] . ' m sets * ' . $ac['n-1'] . ' n sets)<br>';
echo 'cycle disconnections: ' . $gge['c'] . '<br>'; /* debugging */

if ($ggDisplay) {
  echo '<p>grid graph main array:<br>'; print_r($ggt); echo '</p>';
  echo '<p>f(n) g(n) h(n):<br>';
  for ($i = 1; $i <= $ac['v']; $i++) {
    echo 'tile ' . $i . ' (' . $ggt[$i]['xy']['mn'] . '): f = ' . $ggt[$i]['a*']['f'] . ', g = ' . $ggt[$i]['a*']['g'] . ', h = ' . $ggt[$i]['a*']['h'] . '<br>';
  }
  echo '</p>';
  echo '<p>vertex adjacency of cardinal directions:<br>'; print_r($ggcd); echo '</p>';
  echo '<p>vertex adjacency of ordinal directions:<br>'; print_r($ggod); echo '</p>';

  echo '<p>accounting:<br>'; print_r($ac); echo '</p>';
  echo '<p>perimeters:<br>'; print_r($p['u']); echo '</p>';
  echo '<p>Northern perimeters:<br>'; print_r($p['N']); echo '</p>';
  echo '<p>Western perimeters:<br>'; print_r($p['W']); echo '</p>';
  echo '<p>Southern perimeters:<br>'; print_r($p['S']); echo '</p>';
  echo '<p>Eastern perimeters:<br>'; print_r($p['E']); echo '</p>';
  echo '<p>corners:<br>'; print_r($p['c']); echo '</p>';
  echo '<p>central vertices:<br>'; print_r($cv); echo '</p>';
  echo '<p>cycles:<br>'; print_r($cyan['cy']); echo '</p>';
  echo '<p>crossroads:<br>'; print_r($cyan['cr']); echo '</p>';
  echo '<p>edges hm:<br>'; print_r($gge['hm']); echo '</p>';
  echo '<p>edges vn:<br>'; print_r($gge['vn']); echo '</p>';
}
 
/* ////////////////////////////////////////////////////////////////////////////////////////////////*/
/* The End.                                                                                        */
/* ////////////////////////////////////////////////////////////////////////////////////////////////*/

?>

for Barand: function cbr and if algor cb is my non path finding solution. n-1^2 sets the stage.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.