连续块绘图/移除逻辑 [英] Contiguous Block Plotting/Removal Logic

查看:30
本文介绍了连续块绘图/移除逻辑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've been attempting to write some logic for a program I have been working on and have run into some difficulty.

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

Essentially what I'm creating on my stage programmatically is a 4x4 grid (16 blocks), which looks just like this:

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

The user's task is to plot a contiguous shape onto the grid by clicking on the blocks and their shape should feature no gaps and no diagonally plotted blocks, for example the following would be a legal shape:

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

However, the following shape wouldn't be and would throw out an error to the user in the form of a pop-up graphic:

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

The plotting process for the grid is associated with a 4x4 virtual representation of the grid in Boolean Array form and looks like this:

    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.如果绘图是非法制作的,则此属性将更改回 false,然后邀请用户再次尝试他们的下一个绘图.

As the user clicks and selects a block, changing the colour property to brown, the relevant boolean property in my 'virtual grid representation' array will change from false to true. If a plot is illegally made then this property is changed back to false and the user is then invited to try their next plot again.

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

I have managed to write the code which forces the user to plot a legal shape and works out when an illegal plot has been made, but I now need to write the logic for when a user de-selects a block from an existing legal shape, making it non-contiguous and this is where my problem lies.

这是目前可行的解决方案.

Here is the working solution as it stands.

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

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

Now, for most occurences this logic works and detects illegal plots when adding or removing a block to the shape, however recently I discovered that when I have 5 > blocks in the shape, the logic for detecting an error on removal fails in certain circumstances.

一些形状被声明为真实和合法时(当块被移除时)的例子如下:

A few examples of shapes being declared true and legal when they are not (when a block has been removed) are as follows:

我可以看到,我的 'ppnBlockValid():Boolean' 函数中编写的逻辑需要调整以补偿这些输出.似乎您只能删除一个块,前提是相邻块仍然连接到其他块.虽然这适用于较小的形状,但理论上较大的形状(例如 5 个块或更多块)可以从中间拆分,因此我认为需要调整代码以解决此问题.

I can see that it is the logic written in my 'ppnBlockValid():Boolean' function that needs adjusting to compensate for these outputs. It seems you can only remove a block providing that the neighbouring blocks are still joined to something else. While this works for smaller shapes, larger shapes (e.g. 5 blocks or more) can theoretically be split down the middle, so I think the code needs adjusting to account for this.

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

But how? Any help on this would be greatly appreciated.

非常感谢,如果您需要我提供更多信息,请告诉我.

Many thanks in advance and if you need any further information from me please let me know.

干杯,

乔尔

已编辑

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

Thank you very much for providing that enhanced code and explanation @dhc I really appreciate it, but I'm still a little confused about how to implement all this properly.

这是我目前根据您的建议编写的ppnBlockValid"函数代码:

Here is my current 'ppnBlockValid' function code based on your suggestion below:

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;
    }

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

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

I have been using the following shape as my experiment with the code:

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

Based on the above code and example shape, my current trace output is:

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"?对吗?

It appears that despite the shape being contiguous, the code I've tried interpreting from you is finding an additional island, in this case 'Col: 0, Row: 2', before a block removal has even taken place? Is this right?

如果我尝试删除中间(红色)块,此代码当然会输出错误,因为ppnIsland"数组包含 > 1 个岛,但是我认为它没有检测到正确的输出?

This code of course does output an error if I try to remove the middle (red) block as the 'ppnIsland' array contains > 1 island, however I don't think its detecting the correct output?

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

Do I need to cross-reference the 'ppnIsland' and 'ppnAddToExistingIsland' arrays using the indexOf command to check if either element is part of an existing island?

乔尔

推荐答案

您可以在处理时将孤岛"作为单独的列表进行跟踪(在 ppnBlockValid 中).因此,例如(大写是选定的瓷砖):

You could track "islands" as separate lists as you process (in ppnBlockValid). So, for instance (caps are selected tiles):

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.

When you process row one, you create an island for B. When you process row 2, you find B connected to F, so the island becomes [B,F]. Then, when you encounter H, which is not connected, you have two islands: [B,F],[H]. On row 3, your islands would look like [B,F,J],[H,L]. If K were selected, you'd find J and L connected, and you'd consolidate them into one island. You only have to check the current row for connections between islands, so you'd only have to check J and L in this case.

如果你最终只有一个岛,那就没问题,否则就不行.不幸的是,这是一次 n**2 搜索,但您的网格很小,因此可能意义不大.

If you end up with one island, you're fine, otherwise not. This is, unfortunately, an order n**2 search, but your grid is small so it probably doesn't mean much.

<小时>像这样的东西(警告:未调试代码):


Something like this (WARNING: not debugged code):

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 查找岛屿中的瓷砖).

You just have to implement "addNewIsland" and "addToExistingIsland". addNewIsland is easy: just add a new element to the islands array. You may want to use a string ID for the tiles instead of an array (e.g. "01" instead of [0,1]) since it may make checking for existing elements easier (e.g. you can use indexOf to find a tile in an island).

addToExistingIsland 有点棘手:您必须检查任一元素是否是现有岛的一部分,如果是,则合并这些岛.如果没有,新的磁贴就会被附加到岛上.

addToExistingIsland is a bit trickier: you have to check if either element is part of an existing island, and consolidate those islands if so. If not, the new tile just gets appended to the island.

<小时>您不需要维护 3 个数组,只需要维护一个岛数组,在 ppnBlockValid 的末尾,长度 > 1(无效)或更少(有效).


You don't need to maintain 3 arrays, just one island array that, at the end of ppnBlockValid, will either be length > 1 (invalid), or less (valid).

您不能只填充ppnAddNewIsland"和ppnAddToExistingIsland"数组——这些应该是函数.如果 ppnIslands 是一个类变量,那么您的 addNewIsland 函数可能如下所示:

You cannot just populate the "ppnAddNewIsland" and "ppnAddToExistingIsland" arrays-- these should be functions. If ppnIslands is a class variable, then your addNewIsland function may look something like this:

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

...我可能会做的唯一优化是将 who(作为数组 [col,row] 传递)转换为字符串,正如我所提到的,以便更容易地查找岛上是否存在图块.

... the only optimization I'd probably make is to turn who (passed as an array [col,row]) into a character string, as I mentioned, to make finding whether a tile exists in an island easier.

然后,你需要一个类似的函数:

Then, you need a function like:

private function addToExistingIsland( existing_island_tile, new_tile ) {
}

此功能必须:

  • 1.检查这两个图块是否已经存在于同一个岛上(如果存在就返回)
  • 2.检查 new_tile 是否已经在一个岛上(如果是,则将两个岛合并为一个)
  • 3.否则,只需将 new_tile 添加到 existing_island_tile 所属的岛屿

    This function must:

    • 1. check whether these two tiles already exist in the same island (just return if so)
    • 2. check whether new_tile is already on an island (consolidate the two islands into one if so)
    • 3. otherwise, just add new_tile to the island that existing_island_tile is a member of

      这篇关于连续块绘图/移除逻辑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆