【问题标题】:Contiguous Block Plotting/Removal Logic连续块绘图/删除逻辑
【发布时间】:2013-12-10 17:13:26
【问题描述】:

我一直在尝试为我正在开发的程序编写一些逻辑,但遇到了一些困难。

基本上我在舞台上以编程方式创建的是一个 4x4 网格(16 个块),如下所示:

用户的任务是通过单击块在网格上绘制一个连续的形状,并且它们的形状应该具有没有间隙和没有对角绘制的块,例如以下将是合法的形状:

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

网格的绘制过程与布尔数组形式的网格的 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];

当用户单击并选择一个块时,将颜色属性更改为棕色,我的“虚拟网格表示”数组中的相关布尔属性将从 false 变为 true。如果一个情节是非法的,那么这个属性会被改回假,然后用户被邀请再次尝试他们的下一个情节。

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

这是目前的工作解决方案。

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

    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 个 > 块时,检测删除错误的逻辑失败某些情况。

当形状不正确时(当块被移除时)被声明为真实和合法的几个例子如下:

我可以看到,我的 '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;
    }

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

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

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

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' 数组包含 > 1 个岛,此代码当然会输出错误,但我认为它没有检测到正确的输出?

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

乔尔

【问题讨论】:

  • 如果您的目标是不创建岛屿,则在存在极地相邻块时进行简单更改以防止块移除可能是不够的。考虑一个 8 块圆圈:移除北单元并不意味着创建两个岛,因为南单元仍然连接两个岛。假设圆圈的北行表示为 ABC,并且您想要移除 B,您可能需要从单元格 A 遍历邻接直到到达 C。只有这样您才能证明有效的块移除。

标签: actionscript-3 flash logic


【解决方案1】:

您可以在处理时将“岛屿”作为单独的列表进行跟踪(在 ppnBlockValid 中)。因此,例如(帽子是选定的瓷砖):

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

处理第一行时,为 B 创建一个岛。处理第二行时,发现 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 很简单:只需向 island 数组添加一个新元素。您可能希望为瓷砖使用字符串 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”逻辑。

【讨论】:

  • 这听起来像是我需要在我的项目@dhc 中实现的逻辑类型,并且建立在 Atriace 的好建议之上。关于基于(网格)行的“孤岛”搜索,您提到将其写入我现有的“ppnBlockValid”布尔函数中。这是否涉及创建两个新数组,第一个用于连接块,第二个用于非连接块?这在“ppnBlockValid”函数中的 ActionScript3 中看起来如何?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多