用 AppleScript 编写的圣诞灯颜色唯一随机模式的算法

an algorithm for uniquely random patterns of colors for Christmas lights written in AppleScript

这 post 最终是关于为智能圣诞灯创建随机模式文件。

我选择了 7 种颜色(红色、绿色、蓝色、橙色、紫色、白色、深色),我想对其进行半随机化处理。

我有 4 个“框架”,它是一个 5 宽 x 4 高的网格,我想用“随机”颜色填充,没有颜色在我这边,或者直接上下并没有颜色在每个网格位置的帧之间重复。

以下是完全随机帧的示例。完全随机很容易,这就是我要寻找的“无重复”随机数。同样,这些框架不是我最终要寻找的,但它们很接近。此外,以下内容仅供视觉参考,在 AppleScript(或可能任何其他编程语言)中,以下将是列表列表。

P G O R D     D O G D O    P W D O G    W O G B W
W B P D O     P G R G B    R G B O P    D R B P G
R G B D O     R O G O W    P O W D R    O G R W D    
B G W D B     D R G P R    R W D O B    B O G D W

所以这里有一些代码可以让我进入第一帧的第一列:

set colorList to {"R", "G", "B", "D", "O", "W", "P"}
set newColumn to {{}, {}, {}, {}, {}}
set previousColor to {}
set previousColumn to {}


repeat with i from 1 to 4 -- for the 4 frames that I need
    repeat with j from 1 to 5 -- for the 5 columns that each frame needs
        repeat with k from 1 to 4 -- for the 4 values I need in each column
            set isRandom to false
            set theColor to some item of colorList
            if i is not greater than 1 then -- if i is 1 then it is the first frame of the series
                if j is not greater than 1 then -- if J is 1 then it is the first column
                    if k is not greater than 1 then -- if K is 1 then its the first value of the column
                        set end of (item j of newColumn) to theColor
                        
                    else
                        if theColor is not previousColor then -- if k is greater than 1 then ¬
                            --check the color against the previous color, to make sure they are not the same
                            set end of (item j of newColumn) to theColor
                            set previousColor to theColor
                        else
                            repeat until isRandom is true
                                set theColor to some item of colorList
                                if theColor is not previousColor then
                                    set end of (item j of newColumn) to theColor
                                    set isRandom to true
                                end if
                            end repeat
                        end if
                    end if
                end if
            end if
        end repeat
    end repeat
end repeat

我曾尝试更进一步,但每次我这样做时,它都会变成一团乱七八糟的 if-then-else 语句,让我迷失其中。

因此脚本在向列添加颜色之前需要做 3 件事:

  1. 确保列之前的颜色不一样
  2. 确保(如果我们正在处理的列不是第 1 列)颜色与前面的列项目不同,
  3. 确保(如果我们正在处理的帧超出第一帧)颜色与之前帧相同位置的颜色不同。

所以我要问的问题是,他们的处理程序或算法是否是我所缺少的(我不是 CS 专业的)可以使这项任务更直接?

这就是您可能需要了解的有关我的问题的全部信息,但为了参考起见,我正在尝试为圣诞灯创建图案。我脚本中的 colorList 实际上是这样的:

set colorList to {{255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {255, 255, 255}, {255, 128, 0}, {255, 255, 0},{0, 0, 0}}

所以最后我需要一个格式完全像这样的文本文件:

 255, 0, 0
 0, 0, 255
 0, 0, 0
 255, 255, 0

(每行前有一个space字符)

我获取文本文件,将其转换为二进制文件,然后使用一些 Python 将文件发送到灯正在使用的 REST API。 这些灯来自一家名为 Twinkly 的公司。 我只是想让我更轻松地创建一些场景(并希望在此过程中学到一些东西)。

好的,我继续在没有 objC 的情况下完成了这个。我使用的方法是首先构建一个适当大小和形状的空数组,然后 运行 通过数组添加颜色,阻止颜色被添加到更远的相邻单元格。它有一些棘手的方面——通过递归向下钻取嵌套列表,使用引用进行速度优化和列表处理,模拟一个版本的 indexPath 来定位列表元素——但你应该很快就能理解它。

我把剧本的定稿留给了你;这会生成 属性 fullList 中的文本元素列表,您可以将其转换为所需的形式。有什么问题可以在评论里提问。

property colorList : {"Red", "Green", "Blue", "Orange", "Purple", "White", "Dark"}
property fullList : missing value

property columns : 5
property rows : 4
property blocks : 4

my buildEmptyNestedList()
my setColorsRecursivelyInNestedList:(a reference to fullList) withIndexList:{}
return get fullList

on buildEmptyNestedList()
    set fullList to {}
    set fullListRef to a reference to fullList
    repeat blocks times
        set blocksList to {}
        repeat rows times
            set rowsList to {}
            repeat columns times
                set end of rowsList to {}
            end repeat
            copy rowsList to end of blocksList
        end repeat
        copy blocksList to end of fullListRef
    end repeat
end buildEmptyNestedList

on setColorsRecursivelyInNestedList:nestedListElement withIndexList:idxList
    local idx
    
    if lists of nestedListElement is equal to {} then -- bottom level: empty list or list of strings
        set localColors to my filterColorsByList:nestedListElement
        set chosenColor to some item of localColors
        set excludePathsList to my processPathList:idxList
        repeat with aPathList in excludePathsList
            set target to (my getSublistByIndexes:aPathList)
            copy chosenColor to end of target
        end repeat
        set contents of nestedListElement to chosenColor
    else
        set idx to 1
        repeat with aSublist in nestedListElement
            copy idxList to nextIdxList
            set nextIdxList to nextIdxList & idx
            (my setColorsRecursivelyInNestedList:(a reference to (item idx of nestedListElement)) withIndexList:nextIdxList)
            set idx to idx + 1
        end repeat
    end if
end setColorsRecursivelyInNestedList:withIndexList:

on getSublistByIndexes:idxList
    set foundList to item (item 1 of idxList) of fullList
    repeat with idx in (rest of idxList)
        set foundList to item idx of foundList
    end repeat
    return foundList
end getSublistByIndexes:

on getElementFromList:aNestedList byIndexes:idxArray
    set currIdx to item 1 of idxArray
    if (count of idxArray) = 1 then
        return item currIdx of aNestedList
    else
        return my getElementFromList:(item currIdx of aNestedList) byIndexes:(rest of idxArray)
    end if
end getElementFromList:byIndexes:

on processPathList:aPathList
    set pathCheckList to {}
    repeat with i from 1 to count of aPathList
        copy aPathList to tempList
        set item i of tempList to (item i of tempList) + 1
        if item i of tempList ≤ item i of {blocks, rows, columns} then
            copy tempList to end of pathCheckList
        end if
    end repeat
    return pathCheckList
end processPathList:

on filterColorsByList:aList
    set filteredList to {}
    set colorListRef to a reference to colorList
    repeat with aColor in colorListRef
        if aColor is not in aList then
            copy aColor as text to end of filteredList
        end if
    end repeat
    return filteredList
end filterColorsByList: