连续的块绘图/删除逻辑

时间:2013-12-10 17:13:27

标签: actionscript-3 flash logic

我一直在尝试为我一直在努力的程序编写一些逻辑,并遇到了一些困难。

基本上我在我的舞台上以编程方式创建的是一个4x4网格(16个块),看起来就像这样:

4 x 4 grid

用户的任务是通过单击块将连续的形状绘制到网格上,并且它们的形状应该包含无间隙没有对角线绘制的块,例如以下是合法的形式:

legal shape

但是,以下形状不会,并会以弹出图形的形式向用户抛出错误:

illegal shape

网格的绘图过程与布尔阵列形式的网格的4x4虚拟表示相关联,如下所示:

    public static var ppnRowArray1:Array = [false,false,false,false];
    public static var ppnRowArray2:Array = [false,false,false,false];
    public static var ppnRowArray3:Array = [false,false,false,false];
    public static var ppnRowArray4:Array = [false,false,false,false];
    public static var ppnColumnArray:Array = [ppnRowArray1,ppnRowArray2,ppnRowArray3,ppnRowArray4];

当用户单击并选择一个块时,将color属性更改为brown,我的“虚拟网格表示”数组中的相关布尔属性将从false更改为true。如果非法制作地块,则此属性将更改为false,然后邀请用户再次尝试下一个绘图。

我已设法编写强制用户绘制合法形状的代码,并在制作非法绘图时计算出来,但我现在需要编写逻辑,以便当用户从现有内容中取消选择块时合法的形状,使其不连续,这就是我的问题所在。

这是现有的工作解决方案。

        //---------------------------------------------------------------------

    public static function ppnCountSetCells():int
    {
        //Count Each 4x4 Grid Cell
        var count:int = 0;
        for (var row=0; row<=3; row++)
        {
            for (var col=0; col<=3; col++)
            {
                if (ppnColumnArray[col][row])
                {
                    count++;
                }
            }
        }
        return count;
    }

    //---------------------------------------------------------------------

    public static function ppnBlockValid():Boolean
    {
        if (ppnCountSetCells() > 1)
        {
            for (var row=0; row<=3; row++)
            {
                for (var col=0; col<=3; col++)
                {
                    if (ppnColumnArray[col][row] == true)
                    {
                        // Check if we are connected to another set square
                        var validNeighbours:int = 0;

                        // Check North
                        if (row > 0)
                        {
                            if (ppnColumnArray[col][row - 1] == true)
                            {
                                validNeighbours++;
                            }
                        }

                        // Check South
                        if (row < 3)
                        {
                            if (ppnColumnArray[col][row + 1] == true)
                            {
                                validNeighbours++;
                            }
                        }

                        // Check West
                        if (col > 0)
                        {
                            if (ppnColumnArray[col - 1][row] == true)
                            {
                                validNeighbours++;
                            }
                        }

                        //-----------------------------------------------------------------------------------------
                        // Check East
                        if (col < 3)
                        {
                            if (ppnColumnArray[col + 1][row] == true)
                            {
                                validNeighbours++;
                            }
                        }

                        //-----------------------------------------------------------------------


                        if (validNeighbours < 1)
                        {
                            return false;
                        }

                        //-----------------------------------------------------------------------
                    }
                }
            }
        }
        return true;
    }

    //---------------------------------------------------------------------

function addBlock(e:MouseEvent):void
            {
                //trace("You Have Clicked On Grid Block Number: " + e.currentTarget.id);

                if (InterfaceButtons.panelOpen == false)
                {
                    //Listen to see if the block click is adjoining and pass back to see if it is valid on the grid
                    var col:int = (e.currentTarget.id - 1) % 4;
                    var row:int = (e.currentTarget.id - 1) / 4;

                    ppnColumnArray[col][row] = true;

                    addOrRemove = "add";

                    ppnBlockValid();

                    //Get the Block Valid Result (True or False) and pass it into a Boolean variable to use later
                    ppnGridError = ppnBlockValid();


                    trace("Is This Valid? " + ppnBlockValid());

                    //----------------------------------------------------------------------------------------------

                    //Push Blocks Selected into Array
                    ppnShapeArray[e.currentTarget.id] = true;
                    trace(ppnShapeArray);

                    //----------------------------------------------------------------------------------------------

                    //Add 1 to the block count which directly effects the final outcome depending on ++ or --
                    ppnBlocksSelected++;

                    PlantPopNitDesignPlot.ppnPlotMade = false;

                    //Hide Block to Reveal Brown One
                    e.currentTarget.alpha = 0;

                    //-----------------------------------------------------------------------------------------------

                    //Output an error if one is present on Click based on gridError Boolean Variable
                    ppnOutputAddError();

                    if (ppnGridError == false)
                    {
                        //Restore the block's alpha property as it isn't allowed to be selected, removing counter by one -- and changing final output accordingly
                        e.currentTarget.alpha = 1;
                        ppnBlocksSelected--;
                        ppnColumnArray[col][row] = false;
                        ppnShapeArray[e.currentTarget.id] = false;
                        ppnPopulateTotalSiteUnitsTxt();
                    }

                    //Update final total
                    ppnPopulateTotalSiteUnitsTxt();

                    //Call again to do dynamic colour font change should total exceed 10
                    ppnPopulateTotalSiteUnitsTxt();

                    //Added in to make sure it executes every time if an error is made.
                    if (ppnGridError == true)
                    {
                        e.currentTarget.removeEventListener(MouseEvent.CLICK, addBlock);
                        e.currentTarget.addEventListener(MouseEvent.CLICK, removeBlock);

                    }
                }

            }

            function removeBlock(e:MouseEvent):void
            {
                if (InterfaceButtons.panelOpen == false)
                {

                    var col:int = (e.currentTarget.id - 1) % 4;
                    var row:int = (e.currentTarget.id - 1) / 4;

                    ppnColumnArray[col][row] = false;

                    addOrRemove = "remove";

                    ppnBlockValid();

                    ppnGridError = ppnBlockValid();

                    trace("Is This Removal Valid? " + ppnBlockValid());

                    //trace("You Have Clicked On Grid Block Number: " + e.currentTarget.id);

                    e.currentTarget.alpha = 1;

                    ppnShapeArray[e.currentTarget.id] = false;
                    //trace("ppnShapeArray - " + ppnShapeArray);

                    //---------------------------------------------------------------------

                    ppnBlocksSelected--;

                    PlantPopNitDesignPlot.ppnPlotMade = false;

                    //Output an error if one is present on Click based on gridError Boolean Variable
                    ppnOutputRemoveError();

                    if (ppnGridError == false)
                    {
                        //Restore the block's alpha property as it isn't allowed to be selected, removing counter by one -- and changing final output accordingly
                        e.currentTarget.alpha = 0;
                        ppnBlocksSelected--;
                        ppnColumnArray[col][row] = true;
                        ppnShapeArray[e.currentTarget.id] = true;
                        ppnPopulateTotalSiteUnitsTxt();

                    }

                    //Update Final Total
                    ppnPopulateTotalSiteUnitsTxt();

                    //Call again to do dynamic colour font change should total falls below 10
                    ppnPopulateTotalSiteUnitsTxt();

                    //Added in to make sure it executes every time.
                    if (ppnGridError == true)
                    {
                        e.currentTarget.addEventListener(MouseEvent.CLICK, addBlock);
                        e.currentTarget.removeEventListener(MouseEvent.CLICK, removeBlock);

                    }
                }
            }
        }
    }

    //---------------------------------------------------------------------

现在,对于大多数情况,这个逻辑在向形状添加或移除块时会起作用并检测非法图,但是最近我发现当我有5个&g​​t;在某些情况下,形状中的块,用于检测删除错误的逻辑失败。

一些形状被声明为真实且合法的例子(如果一个块被删除)如下:

enter image description here

enter image description here

enter image description here

我可以看到它是我的'ppnBlockValid():Boolean'函数中编写的逻辑,需要调整以补偿这些输出。看起来你只能移除一个块,前提是相邻的块仍然与其他块相连。虽然这适用于较小的形状,但理论上较大的形状(例如5个或更多个)可以在中间分割,所以我认为代码需要调整以解释这个问题。

但是怎么样?对此的任何帮助将不胜感激。

非常感谢,如果您需要我的任何进一步信息,请告诉我。

干杯,

乔尔


被修改

非常感谢您提供增强的代码和解释@dhc我真的很感激,但我仍然对如何正确实现所有这些感到困惑。

以下是我目前的'ppnBlockValid'功能代码,基于您的建议:

public static function ppnBlockValid():Boolean
    {
        ppnIslands = [];
        ppnAddNewIsland = [];
        ppnAddToExistingIsland = [];

        if (ppnCountSetCells() > 1)
        {
            for (var row=0; row<=3; row++)
            {
                for (var col=0; col<=3; col++)
                {
                    if (ppnColumnArray[col][row] == true)
                    {
                        var addedToIsland = false;

                        // Check if we are connected to another set square
                        var validNeighbours:int = 0;

                        // Check North
                        if (row > 0)
                        {
                            if (ppnColumnArray[col][row - 1] == true)
                            {
                                validNeighbours++;
                            }

                            //----------------------------------

                            //ISLAND CHECK
                            if (ppnColumnArray[col][row - 1])
                            {
                                ppnAddToExistingIsland.push([col,row - 1],[col,row]);
                                addedToIsland = true;
                            }
                        }

                        // Check South
                        if (row < 3)
                        {
                            if (ppnColumnArray[col][row + 1] == true)
                            {
                                validNeighbours++;
                            }
                        }

                        // Check West
                        if (col > 0)
                        {
                            if (ppnColumnArray[col - 1][row] == true)
                            {
                                validNeighbours++;
                            }

                            //----------------------------------

                            //ISLAND CHECK
                            if (ppnColumnArray[col - 1][row])
                            {
                                ppnAddToExistingIsland.push([col - 1,row],[col,row]);
                                addedToIsland = true;
                            }
                        }

                        //-----------------------------------------------------------------------------------------
                        // Check East
                        if (col < 3)
                        {
                            if (ppnColumnArray[col + 1][row] == true)
                            {
                                validNeighbours++;
                            }
                        }

                        //-----------------------------------------------------------------------

                        if (! addedToIsland)
                        {
                            ppnIslands.push([col,row]);
                        }

                        //-----------------------------------------------------------------------

                        if (ppnIslands.length >= 2 && addOrRemove == "remove")
                        {
                            trace("TWO ISLANDS HAVE BEEN FORMED AND AN ERROR SHOULD BE OUTPUT");
                            validNeighbours--;
                        }

                        /**/
                        //return (ppnIslands.length<=1);// 0 islands is valid also!

                        //-----------------------------------------------------------------------

                        if (validNeighbours < 1)
                        {
                            return false;
                        }

                        //-----------------------------------------------------------------------
                    }
                }
            }
        }

        return true;
    }

    //--------------------------------------------------------------------- 

我一直在使用以下形状作为我的代码实验:

enter image description here

根据上面的代码和示例形状,我当前的跟踪输出是:

ppnIsland = |0,0|,0,2
ppnAddNewIsland = 
ppnAddToExistingIsland = 0,0, 1,0, 1,0, 2,0, 2,0, 3,0, 1,0, 1,1, 1,1, 1,2, 0,2, 1,2, 1,2, 2,2, 2,2, 3,2

看起来,尽管形状是连续的,我试过从你那里解释的代码是找到一个额外的岛,在这种情况下'Col:0,Row:2',甚至在删除块之前?这是对的吗?

如果我尝试删除中间(红色)块,这个代码当然会输出错误,因为'ppnIsland'数组包含&gt; 1岛,但我不认为它检测到正确的输出?

我是否需要使用indexOf命令交叉引用'ppnIsland'和'ppnAddToExistingIsland'数组来检查这两个元素是否属于现有岛?

乔尔

1 个答案:

答案 0 :(得分:0)

您可以在处理时将“岛屿”跟踪为单独的列表(在ppnBlockValid中)。所以,例如(上限是选定的图块): 的

a B c d
e F g H
i J k L
m n o p

当您处理第一行时,为B创建一个岛。当您处理第2行时,您会发现B连接到F,因此该岛变为[B,F]。然后,当你遇到没有连接的H时,你有两个岛:[B,F],[H]。在第3行,您的岛屿看起来像[B,F,J],[H,L]。如果选择了K,你会发现J和L已连接,你可以将它们合并到一个岛上。您只需要检查当前行之间的岛屿之间的连接,因此在这种情况下您只需要检查J和L.

如果你最终得到一个岛屿,你就没事了,否则没有。不幸的是,这是一个订单n ** 2搜索,但你的网格很小,所以它可能没有多大意义。

<小时/> 这样的事情(警告:未调试的代码):

private var islands : Array;

public function ppnBlockValid():Boolean {
    islands = [];
    if (ppnCountSetCells() > 1) {

        for (var row=0; row<=3; row++) {
            for (var col=0; col<=3; col++) {
                if (ppnColumnArray[col][row]) {
                    var addedToIsland = false;

                    // Check if we are connected to another set square

                    // Check North
                    if (row > 0) {
                        if (ppnColumnArray[col][row-1]) {
                            addToExistingIsland([col,row-1],[col,row]);
                            addedToIsland = true;
                        }
                    }

                    // Check South - not needed since next row will catch this on North

                    // Check West
                    if (col > 0) {
                        if (ppnColumnArray[col-1][row]) {
                            addToExistingIsland([col-1,row],[col,row]);
                            addedToIsland = true;
                        }
                    }

                    // Check East - not needed since next col will catch this on West

                    if (!addedToIsland) { addNewIsland([col,row]); }
                }
            }
        }

    }
    return (islands.length<=1);    // 0 islands is valid also!
}

您只需要实现“addNewIsland”和“addToExistingIsland”。 addNewIsland很简单:只需在islands数组中添加一个新元素即可。您可能希望使用字符串ID而不是数组(例如“01”而不是[0,1]),因为它可以更容易地检查现有元素(例如,您可以使用indexOf在岛中查找图块)。

addToExistingIsland有点棘手:您必须检查其中一个元素是否属于现有岛屿,如果是,则合并这些岛屿。如果没有,新的瓷砖就会附加到岛上。

<小时/> 你不需要维护3个数组,只需要一个岛数组,在ppnBlockValid的末尾,它将是> 1(无效)或更少(有效)。

你不能只填充“ppnAddNewIsland”和“ppnAddToExistingIsland”数组 - 这些应该是函数。如果ppnIslands是一个类变量,那么你的addNewIsland函数可能如下所示:

private function addNewIsland(who) {
    ppnIslands.push([who]);
}

...我可能做的唯一优化是将who(作为数组[col,row]传递)转换为字符串,如我所提到的,以便更容易找到某个区域中是否存在一个区块。

然后,你需要一个像:

这样的功能
private function addToExistingIsland( existing_island_tile, new_tile ) {
}

此功能必须:

  • 1。检查这两个瓷砖是否已存在于同一个岛上(如果是这样,则返回)
  • 2。检查new_tile是否已经在岛上(如果是这样,将两个岛合并为一个)
  • 3。否则,只需将new_tile添加到existing_island_tile所属的岛屿
我提供的代码应该可以正常工作 - 尽管如果你想快速退出隔离的瓷砖,你可以添加回“validNeighbours”逻辑。