元胞自动机的多元投票


31

细胞自动机中有一个非常重要的问题,称为多数问题

多数问题或密度分类任务是找到准确执行多数投票的一维元胞自动机规则的问题。

...

给定一个总状态为i + j的两态细胞自动机的配置,其中i处于零状态,j处于一个状态,对投票问题的正确解决方案必须最终将所有单元设置为零i> j,并且如果i <j,最终必须将所有单元格设置为1。如果i = j,则未指定所需的最终状态。

尽管已经证明,没有任何一种细胞自动机可以在所有情况下解决多数问题,但是有许多规则可以在大多数情况下解决。Gacs-Kurdyumov-Levin自动机在随机初始条件下的准确度约为78%。GKL规则并不复杂:

  • 半径为3,表示该单元格的新状态取决于之前的7个单元格:自身,右侧的3个单元格和左侧的3个单元格。
  • 如果当前是一个单元格O,则其新状态是其自身的大部分状态,该单元格位于其左侧,而单元格3向左移动。
  • 如果当前是一个单元格1,则其新状态是其自身的大部分状态,该单元格位于其右侧,而单元格3则位于其右侧。

这是一个例子:

0 1 0 1 1 1 0 1 1 0 1 0 0 1
0 1 1 1 1 1 1 1 0 0 1 1 0 0
0 1 1 1 1 1 1 1 1 0 1 0 0 0
0 1 1 1 1 1 1 1 0 1 0 1 0 0
0 1 1 1 1 1 1 0 1 0 1 0 1 0
0 1 1 1 1 1 0 1 0 1 0 1 1 1
1 1 1 1 1 0 1 0 1 1 1 1 1 1
1 1 1 1 0 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1

在此示例中,元胞自动机正确地计算出8>6。其他示例花费更长的时间,并在此期间产生一些凉爽的图案。以下是我随机发现的两个示例。

0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 1 1 0 1 0 0 1 1 1
1 0 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 0 0 1 1

将其带入新的高度

据我的互联网研究显示,关于多数问题的几乎所有学术研究都是使用2状态CA进行的。在这个挑战,我们要扩大多数问题三态的CA。我称这个为多元化问题复数(或相对多数)是指其中一个选项的票数超过每个选项的票数,但不一定是所有票数的多数。

问题陈述

  1. 有一个半径为3的三态一维元胞自动机。
  2. 151个具有圆形边界条件的像元。
  3. 仅在3个状态中的1个具有严格的多个条件的情况下,才为这些单元格提供随机的起始状态。“随机”是指每个单元的独立均匀分布。
  4. 规则的准确性是(有效)随机初始条件的百分比,在该条件中,所有单元在10000代内同步到正确状态(具有多个状态)。
  5. 目的是找到一个高精度的规则,

多个边缘情况:具有50/50/51的任何配置都是有效的起始配置(因为有严格的复数),而具有51/51/49的任何配置都是无效的(因为没有严格的复数)。

搜索空间为3 ^ 3 ^ 7(〜3e1043),远远超出任何详尽搜索的范围。这意味着您将需要利用其他技术(例如遗传算法)来解决此问题。它还需要一些人为工程。

10000条生成规则可能会更改,具体取决于人们找到的规则的运行时间/准确性。如果它太低而无法允许合理的收敛速度,那么我可以提高它。另外,我可以降低它作为领带。

获奖

获胜者是在所有参赛者中提交最高半径3 CA规则的人。

您提交的内容应包括...

  • 规则说明(必要时使用Wolfram代码
  • 准确率和样本量
  • 关于如何发现规则的合理大小的解释,包括为解决规则而编写的程序或任何“手动”工程。(这是最有趣的部分,因为其他所有内容都只是原始数字。)

先前的工作

  • 由A纸张瑞伊莱和波拉克,描述他们如何演变有86%的准确度2状态规则。
  • 本文使用r = 3、149个单元的2状态CA。但是,它并没有尝试解决多数问题,而是找到了快速产生交替的1所有0模式的规则。尽管存在这些差异,但我怀疑许多技术都是相似的。
  • Wolz和de Oliviera撰写的一份(不是很有帮助的,因为它位于付费专栏后面),目前拥有两州记录

令我感到失望/惊讶的是,这与多元化投票无关。
2016年

2
@cat我实际上感觉像是。每个单元的状态都可以代表其“投票”(3个候选人中的1个选择),目标是确定选举的获胜者。
PhiNotPi

2
这是一个有趣的代码挑战。我不是高尔夫球手,因此看到这些难题总是很高兴的。
Draco18s

Answers:


6

有点GKL加爬坡,61.498%

  • 如果一个单元格为0,请查看左侧的单元格3,左侧的1和自身。将值设置为多数。如果是平局,请保持原样。

  • 如果单元格为1,请查看右侧的单元格3,右侧的1和自身。将值设置为多数。如果是平局,请保持原样。

  • 如果一个单元格是2,请查看左侧的单元格2,右侧的2和右侧的3。将值设置为多数。如果是平局,请保持原样。

我在100000个总分中拿到59453,占59.453%

一些变异和爬山导致61498/100000 = 61.498%。

我可能会再测试一些,以后再发布更多信息。


3
您可能应该包括实际的61.498%规则,以便人们可以对其进行验证。
Martin Ender

您可能应该进行(将要)进行的测试。
暴民埃里克(Erik the Outgolfer)'16

5

“只丢2s做GKL”-55.7%

很难猜出什么是好规则,所以我试图至少提出分数超过1/3的东西。策略是尝试在多数状态为0或1时获得正确的答案,并在2的情况下接受损失。它在100,000次试验中得分为56.5%,这在某种程度上比乘以78%( GKL得分)* 2/3(答案为0或1时的时间分数)= 52%。

更具体地说,该策略如下:

  • 如果单元格为0或1,则采用GKL策略中的3个单元格中的大多数,但忽略任何相邻的2。如果是平局,则保持该单元格不变。
  • 如果单元格为2,则在整个邻域中选择0或1中的较大者。如果是平局,则选择最左边的值为0或1。如果所有邻居均为2,则保持2。

我使用以下代码进行测试:

#include <iostream>
#include <algorithm>
#include <string.h>
#include <random>
#include <cassert>

#define W 151
#define S 3
#define R 3

typedef int state;

struct tape {
    state s[R+W+R];
    state& operator[](int i) {
        return s[i + R];
    }
    template<typename Rule> void step(Rule r) {
        for(int i = 0; i < R; i++) s[i] = s[W + i];
        for(int i = 0; i < R; i++) s[R + W + i] = s[R + i];
        for(int i = 0; i < W; i++) {
            s[i] = r(s + R + i);
        }
        memmove(s + R, s, W * sizeof(*s));
    }

    state unanimous() {
        state st = (*this)[0];
        for(int i = 1; i < W; i++) {
            if((*this)[i] != st)
                return -1;
        }
        return st;
    }
};

std::ostream& operator<<(std::ostream& s, tape& t) {
    for (int i = 0; i < W; i++)
        s << t[i];
    return s;
}

state randomize(tape& t) {
    static std::mt19937 rg(390332);
    begin:
    int c[S]{};
    for(int i = 0; i < W; i++) {
        state s = rg() % S;
        c[s]++;
        t[i] = s;
    }
    state* smax = std::max_element(c, c + R);
    int nmaj = 0;
    for (int n : c) nmaj += n == *smax;
    if (nmaj > 1) goto begin;
    return smax - c;
}

template<bool PrintSteps, typename Rule> int simulate(Rule r, int trials, int giveup) {
    int successes = 0;
    for(state s = 0; s < S; s++) {
        state t[2 * R + 1];
        for(int i = 0; i <= 2 * R; i++) t[i] = s;
        assert(r(t + R) == s);
    }
    while(trials--) {
        tape tp;
        state desired = randomize(tp);
        int steps = giveup;
        while(steps--) {
            tp.step(r);
            state u = tp.unanimous();
            if(~u) {
                bool correct = u == desired;
                if(PrintSteps) {
                    std::cout << correct << ' ' << giveup - steps << '\n';
                }
                successes += correct;
                break;
            }
        }
    }
    return successes;
}


struct ixList {
    int n;
    int i[2 * R + 1];
};



state rule_justTossOutThe2sAndDoGKL(const state* t) {
    const ixList ixl[] = {
        { 3,{ -3, -1, 0 } },
        { 3,{ 0, 1, 3 } },
        { 6,{ -3, -2, -1, 1, 2, 3 } } 
    };
    int c[S]{};
    for (int i = 0; i < ixl[*t].n; i++)
        c[t[ixl[*t].i[i]]]++;
    if (c[0] > c[1]) return 0;
    if (c[1] > c[0]) return 1;
    if (*t < 2) return *t;
    for (int i = -R; i <= R; i++)
        if (t[i] < 2) return t[i];
    return 2;
}

int main()
{
    int nt = 100000;
    int ns = simulate<false>(rule_justTossOutThe2sAndDoGKL, nt, 10000);

    std::cout << (double)ns / nt << '\n';
    return 0;
}

该分数高于您的预期,因为它随着世代的限制而增加。GKL的78%分数实际上只有几百个左右的极小极限。相比之下,10,000 gen将使GKL的准确率更高,可能与您获得的结果一致。
PhiNotPi

2

“窃取最好的东西并加以发展”,

编辑:在当前状态下,此答案不是找到更好的模式,而是找到更好的随机样本。

该答案通过将所有状态枚举为三进制数字(最低有效数字在前)来编码/解码解决方案。解决方案占59.2%:

000000000010010010000000000000000000000000000000000000000000010000010000110000000
000000000010010010000000000111111101111111111111111111000011000010011011000011010
000000000012010011001000000021111111111120111211111111000000000000011010000010000
000011000010022110000000202000000002000000000020000000001010000000011011000011010
020000000010010010001000000111101111111111111111111111010011000011111111010011010
000000000010010010000000000111111111101111111111112111000011010110111011010011011
000000000010010010000000000010000000000000000100002011000000000100011010020010000
000020020010010010000200000111102111111111111111111101000011010010111011000011011
000100000010010010000000000121111111111111111111111111000210000012011011002011010
000000000010010110000000000111112112111111111001111111000010000010011011000011010
000000000010010120000200000111211111111111111111110111110011010011100111010011011
000000000010010010000000000011111111111111111111111111000011010010111211210012020
010000000010010010020100020111110111111111111111111110010111010011011111010111011
002000000010010010000000000111110111111111211111111111001111111111111111111111111
000000000110010010000000000111111111111111211111111111010111011111111111011111011
001000000010010010000000000011111101111111111111110111000011010010111011010011010
001000000010010110000000000111111111111111102111110111010111011111111111011111101
000000000210010010000000000111111111111111111111011111010011010011111111010111011
000000000010010010000000000112111111111111111111101011000000000000011010000010000
000000000010010010000000000111111111111111111111111111000011010010111011010011011
000200000012010010000000000111111111111112111111111111000010000210011211001011010
000000000010010211000002000111111111111111111111111111000001010010111011010011010
000021200010210010000101100111111111111211111110110211010111021111111101010111111
000000000010010010000000000111111111111101111111111111010011010111111111010110021
000200000010010010000000010111111111101111111121112111000210001010011011000011010
000000000010010010000000000111111111111111111111111111210011010021111111010111011
000020000010010010000000000111111111111111111111111111000011010010121011010011012

该答案是使用以下代码从feersum的55.7%演变而来的。此代码需要libop,这是我个人的C ++标头库。安装非常简单,只需git clone https://github.com/orlp/libop在与保存程序相同的目录中进行即可。我建议使用进行编译g++ -O2 -m64 -march=native -std=c++11 -g。为了加快开发速度,我还建议通过在上运行上述命令来预编译libop libop/op.h

#include <cstdint>
#include <algorithm>
#include <iostream>
#include <cassert>
#include <random>

#include "libop/op.h"

constexpr int MAX_GENERATIONS = 500;
constexpr int NUM_CELLS = 151;

std::mt19937_64 rng;

double worst_best_fitness;

// We use a system with okay-ish memory density. We represent the ternary as a
// 2-bit integer. This means we have 32 ternaries in a uint64_t.
//
// There are 3^7 possible states, requiring 4374 bits. We store this using 69
// uint64_ts, or little over half a kilobyte.

// Turn 7 cells into a state index, by encoding as ternary.
int state_index(const int* cells) {
    int idx = 0;
    for (int i = 0; i < 7; ++i) {
        idx *= 3;
        idx += cells[6-i];
    }
    return idx;
}

// Get/set a ternary by index from a 2-bit-per-ternary encoded uint64_t array.
int get_ternary(const uint64_t* a, size_t idx) {
    return (a[idx/32] >> (2*(idx % 32))) & 0x3;
}

void set_ternary(uint64_t* a, size_t idx, int val) {
    assert(val < 3);
    int shift = 2*(idx % 32);
    uint64_t shifted_val = uint64_t(val) << shift;
    uint64_t shifted_mask = ~(uint64_t(0x3) << shift);
    a[idx/32] = (a[idx/32] & shifted_mask) | shifted_val;
}


struct Rule {
    uint64_t data[69];
    double cached_fitness;

    Rule(const char* init) {
        cached_fitness = -1;
        for (auto i : op::range(69)) data[i] = 0;
        for (auto i : op::range(2187)) set_ternary(data, i, init[i] - '0');
    }

    double fitness(int num_tests = 1000);

    Rule* random_mutation(int num_mutate) const {
        auto new_rule = new Rule(*this);

        auto r = op::range(2187);
        std::vector<int> indices;
        op::random_sample(r.begin(), r.end(),
                          std::back_inserter(indices), num_mutate, rng);

        for (auto idx : indices) {
            set_ternary(new_rule->data, idx, op::randint(0, 2, rng));
        }

        new_rule->cached_fitness = -1;
        return new_rule;
    }

    int new_state(const int* cells) const {
        return get_ternary(data, state_index(cells));
    }

    void print_rule() const {
        for (auto i : op::range(2187)) {
            std::cout << get_ternary(data, i);
            if (i % 81 == 80) std::cout << "\n";
        }
    }
};


struct Automaton {
    uint64_t state[5];
    int plurality, generation;

    Automaton() : generation(0) {
        for (auto i : op::range(5)) state[i] = 0;

        int strict = 0;
        while (strict != 1) {
            int votes[3] = {};
            for (auto i : op::range(NUM_CELLS)) {
                int vote = op::randint(0, 2, rng);
                set_ternary(state, i, vote);
                votes[vote]++;
            }

            // Ensure strict plurality.
            plurality = std::max_element(votes, votes + 3) - votes;
            strict = 0;
            for (auto i : op::range(3)) strict += (votes[i] == votes[plurality]);
        }
    }

    void print_state() {
        for (int i = 0; i < 151; ++i) std::cout << get_ternary(state, i);
        std::cout << "\n";
    }

    bool concensus_reached() {
        int target = get_ternary(state, 0);
        for (auto i : op::range(NUM_CELLS)) {
            if (get_ternary(state, i) != target) return false;
        }

        return true;
    }

    void next_state(const Rule& rule) {
        uint64_t new_state[5] = {};

        std::vector<int> cells;
        for (auto r : op::range(-3, 4)) {
            cells.push_back(get_ternary(state, (r + NUM_CELLS) % NUM_CELLS));
        }

        for (auto i : op::range(NUM_CELLS)) {
            set_ternary(new_state, i, rule.new_state(cells.data()));
            cells.erase(cells.begin());
            cells.push_back(get_ternary(state, (i + 4) % NUM_CELLS));
        }

        for (auto i : op::range(5)) state[i] = new_state[i];
        generation++;
    }
};


double Rule::fitness(int num_tests) {
    if (cached_fitness == -1) {
        cached_fitness = 0;
        int num_two = 0;
        for (auto test : op::range(num_tests)) {
            Automaton a;
            while (a.generation < MAX_GENERATIONS && !a.concensus_reached()) {
                a.next_state(*this);
            }

            if (a.generation < MAX_GENERATIONS &&
                get_ternary(a.state, 0) == a.plurality &&
                a.plurality == 2) num_two++;

            cached_fitness += (a.generation < MAX_GENERATIONS &&
                               get_ternary(a.state, 0) == a.plurality);

            if (cached_fitness + (num_tests - test) < worst_best_fitness) break;
        }

        if (num_two) std::cout << cached_fitness << " " << num_two << "\n";

        cached_fitness;
    }

    return cached_fitness;
}



int main(int argc, char** argv) {
    std::random_device rd;
    rng.seed(42); // Seed with rd for non-determinism.

    const char* base = 
        "000000000010010010000000000000000000000000000000000000000000000000010000000000000"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111000000000000011010000010000"
        "000000000010010010000000000000000000000000000000000000000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011011"
        "000000000010010010000000000000000000000000000000000000000000000000011010000010000"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011011"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011010"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111011111111111111111111111111"
        "000000000010010010000000000111111111111111111111111111010111011111111111011111111"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011010"
        "000000000010010010000000000111111111111111111111111111010111011111111111011111111"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000000000000011010000010000"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011011"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011010"
        "000000000010010010000000000111111111111111111111111111010111011111111111011111111"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000010000010011011000011010"
        "000000000010010010000000000111111111111111111111111111010011010011111111010111011"
        "000000000010010010000000000111111111111111111111111111000011010010111011010011012"
    ;

    // Simple best-only.
    std::vector<std::unique_ptr<Rule>> best_rules;
    best_rules.emplace_back(new Rule(base));
    worst_best_fitness = best_rules.back()->fitness();
    while (true) {
        const auto& base = *op::random_choice(best_rules.begin(), best_rules.end(), rng);
        std::unique_ptr<Rule> contender(base->random_mutation(op::randint(0, 100, rng)));

        // Sort most fit ones to the beginning.
        auto most_fit = [](const std::unique_ptr<Rule>& a, const std::unique_ptr<Rule>& b) {
            return a->fitness() > b->fitness();
        };

        if (contender->fitness() >= best_rules.back()->fitness()) {
            std::cout << contender->fitness();
            double contender_fitness = contender->fitness();
            best_rules.emplace_back(std::move(contender));
            std::sort(best_rules.begin(), best_rules.end(), most_fit);
            if (best_rules.size() > 5) best_rules.pop_back();
            std::cout << " / " << best_rules[0]->fitness() << "\n";
            worst_best_fitness = best_rules.back()->fitness();

            if (contender_fitness == best_rules.front()->fitness()) {
                best_rules.front()->print_rule();
            }
        }
    }

    return 0;
}

0

手工编码,57.541%

这实际上仅查看其上方的5个单元格。可以通过增加其范围来改善它。运行了100,000个测试用例。

算法:

If above == 0:
   if to the left there are only 2s or there is a 1 separated by 2s
       next state = 2
   else
       next state = 0
If above == 1:
   if to the right there are only 2s or there is a 0 separated by 2s
       next state = 2
   else
       next state = 1
If above == 2:
   ignore 0s to the left if the 0 is left of a 1 on the left
   ignore 1s to the right if the 1 is right of a 0 on the right
   if the closest 0 on the left is closer than the closest 1 on the right
       next state = 0
   else if the closest 1 on the right is closer than the closest 0 on the left
       next state = 1
   else
       next state = 2

基因:

000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222222222222
000000000222222222000222222111111111111111111111111111222111111111111111111111111
000000000222222222000222222111111111111111111111111111000000000111111111222111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222222222222
000000000222222222000222222111111111111111111111111111222111111111111111111111111
000000000222222222000222222111111111111111111111111111000000000111111111222111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222222222222222222222222222222000000000222222222222222222
000000000222222222000222222111111111111111111111111111222111111111111111111111111
000000000222222222000222222111111111111111111111111111000000000111111111222111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222
000000000222222222000222222111111111111111111111111111000222222111111111111111111
000000000222222222000222222222222222222222222222222222000000000222222222000222222

测试代码:

import java.lang.Math.*
import java.util.*

const val RADIUS = 3;
const val STATES = 3;
const val DIAMETER = 2 * RADIUS + 1
const val TAPE_LENGTH = 151

val CODE_SIZE = pow(STATES.toDouble(), DIAMETER.toDouble()).toInt()

const val GRADE_RUNS = 100000
const val GRADE_MAX_TIME = 10000


operator fun IntArray.inc() : IntArray {
    val next = this.clone()
    var i = 0
    while (i < size) {
        if (this[i] == STATES - 1) {
            next[i] = 0
        } else {
            next[i]++
            break
        }
        i++
    }
    return next
}
val IntArray.index : Int
    get() {
        var total = 0
        for (i in (size - 1) downTo 0) {
            total *= STATES
            total += this[i]
        }
        return total
    }

interface IRule {
    operator fun get(states : IntArray) : Int
}

fun IntArray.equalsArray(other: IntArray) = Arrays.equals(this, other)

class Rule : IRule {

    constructor(rule : IRule) {
        val start = IntArray(DIAMETER)
        var current = start.clone()

        code = IntArray(CODE_SIZE)
        try {
            do {
                code[current.index] = rule[current]
                current++
            } while (!current.equalsArray(start));
        } catch (e : Throwable) {
            println(Arrays.toString(code))
            println(Arrays.toString(current))
            throw e
        }
    }
    constructor(code : IntArray) {
        this.code = IntArray(CODE_SIZE) { if (it < code.size) code[it] else 0 }
    }

    val code : IntArray

    override fun get(states: IntArray) : Int {
        return code[states.index]
    }

    override fun toString() : String {
        val b = StringBuilder()
        for (i in 0 until CODE_SIZE) {
            if (i > 0 && i % pow(STATES.toDouble(), RADIUS.toDouble() + 1).toInt() == 0) {
                b.append('\n')
            }
            b.append(code[i])
        }
        return b.toString()
    }

    fun grade() : Double {
        var succeeded = 0
        for (i in 0 until GRADE_RUNS) {
            if (i % (GRADE_RUNS / 100) == 0) {
                println("${i/(GRADE_RUNS / 100)}% done grading.")
            }
            var tape : Tape
            do {
                tape = Tape()
            } while (tape.majority() == -1);
            val majority = tape.majority()
            val beginning = tape
            var j = 0
            while (j < GRADE_MAX_TIME && !tape.allTheSame()) {
                tape = tape.step(this)
                j++
            }
            if (tape.stabilized(this) && tape.majority() == majority) {
                succeeded++
            }/* else if (beginning.majority() != 2) {
                println(beginning.majority())
                tape = beginning
                for (j in 1..100) {
                    println(tape)
                    tape = tape.step(this)
                }
                println(tape)
            }*/
        }
        return succeeded.toDouble() / GRADE_RUNS
    }

}

fun getRandomState() : Int {
    return (random() * STATES).toInt()
}

class Tape(val tape : IntArray) {

    constructor() : this(IntArray(TAPE_LENGTH) { getRandomState() } )

    fun majority() : Int {
        val totals = IntArray(STATES)

        for (cell in tape) {
            totals[cell]++
        }

        var best = -1
        var bestScore = -1

        for (i in 0 until STATES) {
            if (totals[i] > bestScore) {
                best = i
                bestScore = totals[i]
            } else if (totals[i] == bestScore) {
                best = -1
            }
        }

        return best
    }

    fun allTheSame() : Boolean {
        for (i in 1 until TAPE_LENGTH) {
            if (this[i] != this[0]) {
                return false
            }
        }
        return true
    }

    operator fun get(index: Int) = tape[((index % TAPE_LENGTH) + TAPE_LENGTH) % TAPE_LENGTH]

    fun step(rule : IRule) : Tape {
        val nextTape = IntArray ( TAPE_LENGTH )

        for (i in 0 until TAPE_LENGTH) {
            nextTape[i] = rule[IntArray(DIAMETER) { this[i + it - RADIUS] }]
        }

        return Tape(nextTape)
    }

    fun stabilized(rule : IRule) = allTheSame() && majority() == step(rule).majority()

    override fun toString() : String {
        val b = StringBuilder()
        for (cell in tape) {
            b.append(cell)
        }
        return b.toString()
    }

}

fun main(args : Array<String>) {
    val myRule = Rule(object : IRule {
        override fun get(states: IntArray): Int {
            if (states[3] == 0) {
                if (states[2] == 1) {
                    return 2
                } else if (states[2] == 0) {
                    return 0
                } else if (states[1] == 1) {
                    return 2
                } else if (states[1] == 0) {
                    return 0
                } else {
                    return 2
                }
            } else if (states[3] == 1) {
                if (states[4] == 0) {
                    return 2
                } else if (states[4] == 1) {
                    return 1
                } else if (states[5] == 0) {
                    return 2
                } else if (states[5] == 1) {
                    return 1
                } else {
                    return 2
                }
            } else {
                if (states[2] == 0) {
                    if (states[4] != 1) {
                        return 0
                    }
                } else if (states[4] == 1) {
                    return 1
                }
                if (states[1] == 0 && states[2] != 1) {
                    if (states[5] != 1) {
                        return 0
                    }
                } else if (states[5] == 1 && states[4] != 0) {
                    return 1
                }
                return 2
            }
        }

    })
    var tape = Tape()
    println(myRule.grade())
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.