I have been trying out all sorts of method in creating a Sudoku Grid and I finally managed to do it. The solution was far more easier than I thought ...
Let's take a look at the source code - SimpleSudokuGrid.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="creationCompleteEvent(event)">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.components.Button;
import spark.events.IndexChangeEvent;
/**
* Holds the data to form a Grid
*/
private var baseGrid:ArrayCollection;
/**
* Boolean to determine if there's a need to recreate the grid
*/
private var needToReset:Boolean = true;
/**
* Number to count the number of failure in the current
* grid data forming.
*/
private var tries:Number = 0;
/**
* Maximum number of failure before recreate.
*/
private static const MAX_TRIES:Number = 9;
/**
* A set of data that will be used to populate the ButtonBar
*/
[Bindable]
private var dummyData:ArrayCollection = new ArrayCollection(
[{label: "1", value:1},
{label: "2", value:2},
{label: "3", value:3},
{label: "4", value:4},
{label: "5", value:5},
{label: "6", value:6},
{label: "7", value:7},
{label: "8", value:8},
{label: "9", value:9}]);
/**
* Upon Create Complete, we will create a Sudoku(9x9) Grid
*/
protected function creationCompleteEvent(e:FlexEvent):void
{
//Will keep recreating the grid if 'needToReset' is
//true
while(needToReset)
{
//Set 'needToReset' to false
needToReset = false;
createGrid();
createNumbers();
}
createGridUI();
btnBar.dispatchEvent(
new IndexChangeEvent(IndexChangeEvent.CHANGE));
}
/**
* Create new (9*9) DataCollection and fill it up with 0.
*/
private function createGrid():void
{
baseGrid = new ArrayCollection();
var tempCol:ArrayCollection;
for(var i:int = 0; i < 9; i ++)
{
tempCol = new ArrayCollection();
for(var j:int = 0; j < 9; j++)
{
tempCol.addItem(0);
}
baseGrid.addItem(tempCol);
}
}
/**
* Start populating the numbers from 1 - 9.
* If 'needToReset' is true, move out of this function.
*/
private function createNumbers():void
{
for(var i:int = 1; i < 10; i ++)
{
insertNumber(i);
if(needToReset)
{
break;
}
}
if(needToReset)
{
return;
}
printNumbers();
}
/**
* Find the empty boxes that you can slot the value
* @param value a value that you are going to slot in
*/
private function insertNumber(value:Number):void
{
//Used to store the rows avaliable in a column
var numbersFree:Array;
//Is the 1st segment taken?
var fromSegment1:Boolean = false;
//Rows that we will need to remove if 1st segment
//is taken
var seg1Array:Array = [0,1,2];
//Is the 2nd segment taken?
var fromSegment2:Boolean = false;
//Rows that we will need to remove if 2nd segment
//is taken
var seg2Array:Array = [3,4,5];
//Is the 3rd segment taken?
var fromSegment3:Boolean = false;
//Rows that we will need to remove if 3rd segment
//is taken
var seg3Array:Array = [6,7,8];
//Stores the current selected row of a column
var rand:Number;
//Stores all the boxes that has the value
var tempArray:Array = new Array();
for(var i:int = 0; i < 9; i ++)
{
//In sudoku, it's divided into 9 (3*3) boxes.
//Each each of this box, the same number can
//appear once.
//Therefore every 3 column, we need to reset
//these 3 values.
if(i % 3 == 0)
{
fromSegment1 = false;
fromSegment2 = false;
fromSegment3 = false;
}
//Find the empty slots of the column first
numbersFree = findEmptySlot(i);
//Remove the rows based on the previous selections
if(fromSegment1)
{
numbersFree = removeSlot(numbersFree, seg1Array);
}
if(fromSegment2)
{
numbersFree = removeSlot(numbersFree, seg2Array);
}
if(fromSegment3)
{
numbersFree = removeSlot(numbersFree, seg3Array);
}
//Remove the rows that have been selected already.
if(tempArray.length > 0)
{
numbersFree = removeSlot(numbersFree, tempArray);
}
//If there are rows left
if(numbersFree.length > 0)
{
//Select one of them
rand = Math.floor(Math.random()
* numbersFree.length);
rand = numbersFree[rand];
//Check which Segment the row number is in
if(rand < 3)
{
fromSegment1 = true;
}else if(rand < 6)
{
fromSegment2 = true;
}else{
fromSegment3 = true;
}
//Add it to the list of boxes with the
//same value
tempArray.push(rand);
//Update the value in the main DataCollection
insertSlot(i, rand, value);
}else{
//else if '0' row is avaliable
//increase the number of failures/tries
tries ++;
//Revert back all the slots
revertSlots(tempArray);
tempArray = new Array();
i = -1;
//Check if it has reach the Maximum tries/failures
if(tries == MAX_TRIES)
{
//Need to recreate the grid again.
tries = 0;
needToReset = true;
return;
}
}
}
}
/**
* Find all the empty rows of a selected Column
* @param col the selected column index
* @return Array of rows that currently has a value of '0'
*/
private function findEmptySlot(col:Number):Array
{
var tempArray:Array = new Array();
var tempArrayCol:ArrayCollection;
tempArrayCol = ArrayCollection(baseGrid.getItemAt(col));
for(var i:int = 0; i < tempArrayCol.length; i ++)
{
if(Number(tempArrayCol.getItemAt(i)) == 0)
{
tempArray.push(i);
}
}
return tempArray;
}
/**
* Compare 2 Arrays.
* @param tempArray1 Array of values
* @param tempArray2 Array of values
* @return the Array of values tempArray1 that are different from
* tempArray2
*/
private function removeSlot(tempArray1:Array, tempArray2:Array):Array
{
var tempArray:Array = new Array();
for(var i:int = 0; i < tempArray1.length; i ++)
{
tempArray.push(tempArray1[i]);
}
for(i = 0; i < tempArray.length; i ++)
{
for(var j:int = 0; j < tempArray2.length; j ++)
{
if(tempArray[i] == tempArray2[j])
{
tempArray.splice(i, 1);
i --;
break;
}
}
}
return tempArray;
}
/**
* Replace a particular slot with a new value.
* @param col selected column
* @param row selected row
* @param value the new value
*/
private function insertSlot(col:Number, row:Number, value:Number):void
{
var tempArrayCol:ArrayCollection;
tempArrayCol = ArrayCollection(baseGrid.getItemAt(col));
tempArrayCol.setItemAt(value, row);
}
/**
* This function will change the newly numbers of the same group
* back to 0. For example, halfway through the number 7, a problem
* has occur, therefore need to replace all the number 7 from the
* first box again. Hence, need to reset all the existing number
* 7 box to '0' first.
* @param tempArray1 Boxes that requires the values to be reset to '0'
*/
private function revertSlots(tempArray1:Array):void
{
var tempArrayCol:ArrayCollection;
for(var i:int = 0; i < tempArray1.length; i ++)
{
tempArrayCol = ArrayCollection(baseGrid.getItemAt(i));
tempArrayCol.setItemAt(0,tempArray1[i])
}
}
/**
* This is merely for debugging purposes. You can use
* this function to track the valus in the (9x9) DataCollection.
*/
private function printNumbers():void
{
var tempArrayCol:ArrayCollection;
var tempArray:Array = new Array();
for(var i:int = 0; i < 9; i ++)
{
tempArray.push("");
}
for(var col:int = 0; col < baseGrid.length; col ++)
{
tempArrayCol =
ArrayCollection(baseGrid.getItemAt(col));
for(var row:int = 0; row < tempArrayCol.length; row ++)
{
tempArray[row] +=
Number(tempArrayCol.getItemAt(row));
}
}
for(i = 0; i < tempArray.length; i ++)
{
trace(tempArray[i]);
}
}
/**
* Create all the buttons base on the values in the ArrayCollection
*/
private function createGridUI():void
{
var btn:Button;
var tempArrayCol:ArrayCollection;
for(var row:int = 0; row < 9; row ++)
{
for(var col:int = 0; col < baseGrid.length; col ++)
{
tempArrayCol =
ArrayCollection(baseGrid.getItemAt(col));
btn = new Button();
btn.width = 30;
btn.height = 30;
btn.label =
Number(tempArrayCol.getItemAt(row)).toString();
btn.name = "btn_" + row + "_" + col;
grpGrid.addElement(btn);
}
}
}
/**
* Upon clicking on one of the buttons of the ButtonBar, we will
* show buttons with the same value with a alpha value of 1 and
* those that are different will have a alpha value of 0.3.
*/
protected function changeEvent(event:IndexChangeEvent):void
{
var tempBar:ButtonBar = ButtonBar(event.target);
var tempObj:* = tempBar.selectedItem;
var selectedValue:Number = -1;
if(tempObj)
{
selectedValue = tempObj.value;
}
var btn:Button;
for (var i:int = 0; i < grpGrid.numElements; i ++)
{
btn = Button(grpGrid.getElementAt(i));
if(Number(btn.label) == selectedValue)
{
btn.alpha = 1;
}else{
btn.alpha = 0.3;
}
}
}
]]>
</fx:Script>
<s:VGroup verticalCenter="0"
horizontalCenter="0"
horizontalAlign="center">
<s:Group id="grpGrid" >
<s:layout>
<s:TileLayout requestedRowCount="9"
requestedColumnCount="9"
horizontalGap="3"
verticalGap="3"/>
</s:layout>
</s:Group>
<s:Label text="Click on the following to show the buttons with the same value."/>
<s:ButtonBar id="btnBar"
dataProvider="{dummyData}"
selectedIndex="-1"
labelField="label"
change="changeEvent(event)"/>
</s:VGroup>
</s:Application>
* Click here for the demo shown in this post.^ Click here for the source files for the demo.
No comments:
Post a Comment