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