Sudoku Solver PHP Part 4

Sudoku Solver PHP Part 4

Categorised in:

Now we’ve found the only way to solve complex Sudoku puzzles is suck it and see, we need a method for displaying and modifying the grid. So let’s start with displaying the grid. Rather than work out the formula’s for cell borders I thought, “Hell I only need to do this once so let’s define the cell boundaries”. As follows:

// This next bit is just for formatting the cells for viewing
	
	$cleft = array(1,4,7, 10,13,16, 19,22,25, 28,31,34, 37,40,43, 46,49,52, 55,58,61, 64,67,70, 73,76,79);
	$ctop = array( 1,2,3,4,5,6,7,8,9, 28,29,30,31,32,33,34,35,36, 55,56,57,58,59,60,61,62,63 );
	$cright = array( 3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60,63,66,69,72,75,78,81 );
	$cbottom = array( 19,20,21,22,23,24,25,26,27, 46,47,48,49,50,51,52,53,54, 73,74,75,76,77,78,79,80,81);

And we use these values to correctly add borders to the grid as follows:

<div class="grid">
<?php

	
	for( $z = 1; $z <= 81; $z++ ) {
		
		$cl = $cr = $ct = $cb = $wc = "";

		if( in_array($z, $cleft) ) $cl = ' cleft ';
		if( in_array($z, $ctop) ) $ct = ' ctop ';
		if( in_array($z, $cright) ) $cr = ' cright ';
		if( in_array($z, $cbottom) ) $cb = ' cbottom ';

		if( strlen($grid[$z]) == 1 ) $wc = ' winner '; 

		echo '<div id="cell' . $z . '" class="cell ' . $cl . $cr . $ct . $cb . $wc . '"><span class="cellvalue">' . $grid[$z] . '</span><div class="gridid">' . $z . '</div></div>';
		
	}
?>
			
</div>

It does so by displaying the grid in order. Each grid displays the possible numbers and in this case also the grid reference. The latter is just for aiding but caused an issue with the later javascript and so we set this to hidden after debug. If the cell is a single number strlen($grid[$z]) == 1 then this is given the class “winner” to make it stand out and also for referencing later.

This is the css required:

.grid {
	display: flex;
	flex-wrap: wrap;
	width: 660px;
	cursor: pointer;
}

.cell {
	width:70px;
	height: 70px;
	border: 1px solid #ccc;
	border-radius: 5px;
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
}

.cleft {
	border-left: 2px solid #000; 
}

.ctop {
	border-top: 2px solid #000; 
}

.cright {
	border-right: 2px solid #000; 
}

.cbottom {
	border-bottom: 2px solid #000; 
}

.winner {
	font-weight: bold;
	font-size: 2rem;
}

.gridid {
	color:#ddd;
	font-size: 1rem;
	font-weight: normal;
	align-self: flex-end;
	display: none;
}

Note the width of the grid and the width of the cells. This allows 9 cells of width 70px, plus the borders. The display:flex and flex-wrap:wrap makes sure that the divs will be inline and wrap onto the next line when the end of the container is reached. This will give us the 9×9 grid.

So now we’ve got the grid, can display the results of the algorithm but we can’t manipulate it. We also need a method of updating the grid with new numbers. I thought about AJAX which would be kind of cool but we can do what we need to do with a simple form. So what we’ll do is show a reduced version of the grid with what the algorithm is working on (eg a pictorial representation of $source). This is simply:

$g = 1;

foreach( $solve as $s ) {
	
	for( $i=0; $i<9; $i++) {
		
		$j = substr($s,$i,1);
		echo '<div id="pblock' . $g . '" class="pblock">'. $j . '</div>';
		$g++;
		
	}
	
	echo ' <br/>';
}

With the following css:

.pblock {
	display: inline-block;
	width: 1rem;
	height: 1rem;
	border:1px solid black;
	font-family: courier;
}

Now we need a method of reconciling the current grid with the puzzle , updating the puzzle, and starting with a blank puzzle for good measure. To do this we’ll introduce some buttons and use javascript to do the rest.

<p></p>
<button id="reconcile">Reconcile</button> <- Transfers all single digits from grid back to start puzzle (Not submitted)
<p></p>
<form id="data" method="post"></form>
<button id="resubmit">Resubmit</button> <- Submit and start with this puzzle.
<form id="reset" method="post">
	<p><input name="action" value="blank" type="hidden" /></p>
	<button type="submit" id="blank">Blank</button> <- Start with a Blank Puzzle.
</form>

I’ll come on to the javascript shortly. Now we need some data entry method. So we’ll put a number keypad on the screen like so:

<div id="numberselector">
<?php
for( $i=1; $i<=9; $i++ ) {
	echo '<span class="nsnumber" id="ns' . $i . '">' . $i . '</span>';
}
?>
</div>

And style it with:

#numberselector {
	position: fixed;
	top: 20px;
	right: 20px;
}
.nsnumber {
	font-size: 2rem;
	padding: 1rem;
	border: 1px solid blue;
	cursor: pointer;
}
.active {
	background: red;
}

So now the visual elements are complete. So for the javascript. First of all, we’re going to use jQuery so we need to include this in the <head> section of our page and I’m just going to link to the Google Repository for this:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

The first part of the script is to define global vars and also add the event listeners for the buttons (or at least the click event):

var active = ""; // Holds the value of the current active number. Global in scope

$( function() { // Only start when jQuery is loaded.

	$('#reconcile').click( function() {
		reconcile();
	}		);
	$('#resubmit').click( function() {
		setform();
	}		);
	
	$('.nsnumber').click( function() {
		$('.nsnumber').removeClass('active');
		$(this).addClass('active');
		active =  this.innerText;
	}		);
	
	$('.grid').click( function(e) {
		 click1 = e.target.parentNode.id; 
		 click2 = e.target.id; 
		 if( click1 == '' ) click = click2;
		 else click = click1;
		 if( click &amp;&amp; active ) {
			 if( $('#' + click + ' .cellvalue').text().indexOf( active ) >= 0 ) {
				 $('#' + click + ' .cellvalue').text( active );
				 $('#' + click).addClass( "winner" );
			 }
		 }
 
	});
});

The grid click is interesting as we have an event that is called when the grid is clicked, but what cell was clicked? By propogating the event we could in fact have two elements clicked. One being the div containing the number and one being the div that surrounds it. So the variable click1 looks for the id of the parent of the target, and click2 looks for the id of the target. What we want is the id of the div element, so if the child element (the number) is clicked then click1 will hold the id of the div and click2 will be empty. Likewise if the div is clicked then click1 will be empty (no container id) and click2 will have the div id. When the number selector is clicked this sets active to this number. This number is then placed in the grid when clicked, but only if the grid allows (eg that cell may be that number).

The functions are as follows:

function reconcile() {
	winners = $('.winner .gridid');
	cells = $('.winner .cellvalue');
	i=0;
	winners.each( function( index ) {
		gid = $(this).text();
		num = cells[i].textContent;
		$('#pblock' + gid).text(num);
		i++;
	});
	

}

What this function does is look for all the cells in the grid that are single numbers, and hence given the class “winner”, traverses the object and places the found number in the puzzle.

function setform() {
	$('#data').empty();
	for( i=1; i<=81; i++ ) {
		$('#data').append('<input type="hidden" value="_" name="puzzle[]" id="puzzlepiece' + i + '" />');
		if( $.isNumeric( $('#pblock' + i).text() ) ) {
			$('#puzzlepiece' + i).attr('value', $('#pblock' + i).text() );
		}
	}
	$('#data').submit();
}

This function first empties the form with the id “data”, eg it removes any child elements within it. It then propogates through the puzzle matrix and creates new hidden form elements with name="puzzle[]" so the result will be an array and with the value of an underscore (which we used in $solve to denote unset.) We then override this value if the puzzle has a number in it. The form is then submitted by the javascript. Now we need a form handler:

// Override the puzzle here if we have $_POST data
if( $_POST ) {

	unset ( $solve );
	// Blank Form
	$solve = array_fill(0,8,"_________");

	$puzzle = $_POST['puzzle'];
	if( is_array( $puzzle ) ) {
		unset( $solve );
		for( $i=0; $i<9; $i++) {	
			for( $j=0; $j<9; $j++) {
				$solve[$i] .= $puzzle[$j + ($i*9)];
			}
		}
	}
	
}

Is quite simple. If we have post data then we reset $solve to give us our blank puzzle. If $_POST['puzzle'] exists then we create a new $solve from this data. Finally as POST data is a bit of a pain when we hit refresh, use a link back to the home to reset everything.

<h1><a href="index.php">Sudoko Solver</a></h1>

The full programme can be tested out here: Suduko Solver PHP

Leave a Reply

Your email address will not be published. Required fields are marked *

Tagged with ,

Available Tags

api barcode ean13 finder foreach functions json oscommerce php programming sudoku themes webp wordpress