善与恶


112

结果-2014年7月19日

山的现任国王是佣兵用户Fabigler!继续提交条目,把他从王位上夺下来!

单击此处查看记分板。

包括2014年7月19日当天或之前提交的课程。所有其他提交的内容将包括在以后的试用中。新结果应该在8月9日左右发布,这样您就有足够的时间了。


哥哥画的插图 由我的哥哥克里斯·雷恩博特(Chris Rainbolt)插图,也是萨凡纳艺术与设计学院的应届毕业生

介绍

天使和魔鬼正在战斗,并且像往常一样,以大地为战场。人类被困在中间,被迫站在一边。一支未知的中立力量会奖励那些不断为失败者而战的人。

游戏

每次试用时,您将被伪随机配对,然后与其他20到30个其他提交进行混洗。每个审判将包括1000发子弹。每回合,您都会收到一个输入,并期望产生输出。您的输出将被记录并评分。此过程将重复1000次。

输入项

您将收到一个代表每个玩家过去投票的参数。轮以逗号分隔。A 0代表在该回合中支持Evil的玩家。A 1代表支持善良的玩家。在试用期内,玩家将始终处于相同的顺序。您的投票将包括在内,但未明确标识。例如:

101,100,100

在此示例中,三轮比赛已经完成,三名选手正在竞争。玩家一向支持好。玩家二总是站在邪恶的一边。玩家3在第一回合中从Good换成了在第二和第三回合中的Evil。

输出量

Java提交

  • good如果要支持Good,则返回字符串。
  • evil如果要支持Evil,请返回字符串。

非Java提交

  • good如果您想支持Good,则将字符串输出到stdout。
  • evil如果要支持Evil,则将字符串输出到stdout。

如果您的程序在此确切的机器上输出或返回任何其他内容,引发异常,未编译或花费一秒钟以上的时间来输出任何内容,则它将被取消资格。

计分

我可以编译所有当前条目后,分数就会发布在Google文档电子表格中,以便于查看。不用担心-只要你们继续提交程序,我就会继续进行试验!

  • 在一个回合中,您以多数票获得3分。
  • 在一个回合中,与少数派保持联系时会得到n-1分,其中n是您站在少数派上的连续次数。

您的分数将是5次试验的中位数。每个审判包括1000发子弹。

可交付成果

非Java提交

您必须提交一个唯一的标题,一个程序和一个将运行程序的Windows命令行字符串。请记住,可以在该字符串后附加一个参数。例如:

  • python Angel.py
    • 请注意,这个没有参数。这是第一回合!为此做好准备。
  • python Angel.py 11011,00101,11101,11111,00001,11001,11001

Java提交

您必须提交一个唯一的标题和一个Java类,以扩展下面编写的抽象Human类。

public abstract class Human {
    public abstract String takeSides(String history) throws Exception;
}

测试中

如果您要测试自己的提交,请按照此处的说明进行操作。

补充笔记

您可以根据需要提交任意数量的不同提交。似乎合谋的作品将被取消参赛资格。对此挑战的作者将是对此事的唯一法官。

程序或Java类的新实例将在每次调用时创建。您可以通过写入文件来保留信息。除了自己的类,您不得修改任何东西的结构或行为。

在审判开始之前,玩家将被洗牌。恶魔和天使将参加每个审判。如果玩家人数是偶数,那么Petyr Baelish也将加入。恶魔为邪恶而战,天使为善,彼得·贝利希选择了伪随机一方。


2
已删除评论,因为它们已过时且应OP要求。如果需要取消任何评论,请通知我。
门把手

7
哇,OP更改了他的用户名。好,那么什么时候显示结果?
Justhalf 2014年

6
@Rainbolt这一定是一场艰苦的工作,挑战了!引起如此多关注的原因是协议和规则的简单性,使其易于访问,同时还允许简单,有效的输入。TL; DR:您的挑战太好了!:D
tomsmeding 2014年

3
@dgel我将发布原始数据,上限,下限,平均值以及折线图,这样我们就可以了解谁在竞争加剧的情况下取得了更好的成绩。
Rainbolt 2014年

6
其中一个广告连播最终以10条条目进行了投票,每次都以相同的方式进行投票。因此,两个用户最终获得大约450,000的满分或“比理想还差一轮”。在其他试验中,相同的条目得分约为1900。平均分数接近2000。由于结果极端不平衡,我决定将一个更有意义的数字作为中位数。我对挑战进行了编辑,以便经过5次试验后,获奖者将是中位数最高的提交者。如果有人认为从均值过渡到中位数是不公平的,或者是错误的选择,请发表评论。
Rainbolt 2014年

Answers:


11

佣兵

总是与上一轮支付最多钱的人站在一边。

考虑到好人的收入在统计上会更高。

package Humans;
public class Mercenary extends Human {
    public String takeSides(String history) {
        // first round random!
        if (history.length() == 0) {
            return Math.random() >= 0.5 ? "good" : "evil";
        }

        String[] rounds = history.split(",");
        String lastRound = rounds[rounds.length - 1];

        double goodMoneyPaid = 0;
        double evilMoneyPaid = 0;
        for (char c : lastRound.toCharArray()) {
                switch (c) {
                case '0':
                    goodMoneyPaid = goodMoneyPaid + 0.2; //statistically proven: good people have more reliable incomes
                    break;
                case '1':
                    evilMoneyPaid++; 
                    break;
                default:
                    break;
                }
        }

        if (goodMoneyPaid > evilMoneyPaid)
        {
            return "good";
        } else {
            return "evil";
        }
    }
}

2
这是第二篇关于金钱的文章。我是否缺少参考文献或其他内容?
Rainbolt

是的,但是这个家伙是个更加邪恶的混蛋。仅仅为了钱而动every抛弃他的朋友。
fabigler 2014年

您的switch语句缺少默认情况下的return语句,导致其无法编译。我加了一个随机的。
Rainbolt 2014年

4
恭喜,山丘之王!我不知道该参赛作品如何获胜。现在要附加一个解释,它有300声望奖励吗?
Rainbolt 2014年

4
可能是一个错误,或者我误解了注释和描述,但是雇佣兵实际上并没有按照原计划做。除第一轮随机投票外,他将始终与邪恶并肩作战,除非在上一轮投票中有不到1/6的人投票支持邪恶。
jaybz 2014年

39

赶时髦的人,露比

if ARGV.length == 0
    puts ["good", "evil"].sample
else
    last_round = ARGV[0].split(',').last
    n_players = last_round.length
    puts last_round.count('1') > n_players/2 ? "evil" : "good"
end

只是因为其他一切都是主流,所以只与上一轮的少数派相提并论。

运行像

ruby hipster.rb

30

彼得·贝利什

您永远不会知道Petyr Baelish站在哪一边。

package Humans;

/**
 * Always keep your foes confused. If they are never certain who you are or 
 * what you want, they cannot know what you are likely to do next.
 * @author Rusher
 */
public class PetyrBaelish extends Human {

    /**
     * Randomly take the side of good or evil.
     * @param history The past votes of every player
     * @return A String "good" or "evil
     */
    public String takeSides(String history) {
        return Math.random() < 0.5 ? "good" : "evil";
    }
}

仅当玩家数为偶数时才包括该条目。这样可以确保始终有多数。


28
显然,在Petyr Baelish的身边。
克苏鲁2014年

2
@Kevin它始终击败大多数机器人。它通常获得27分。
cjfaure

3
@Kevin该条目由挑战的作者提交。它本来就做不好。可以确保始终拥有多数票,因为如果有偶数的玩家,可能会有平局。
Rainbolt 2014年

4
哦,天哪,为什么这个人得票最多?这只是不公平
tomsmeding

3
@tomsmeding不。这是《权力的游戏》大声笑的一句话。
Rainbolt 2014年

29

C ++,元科学家

这基本上和《 The Scientist》相同,但不是整体上都是一轮,而是针对单个玩家。它试图将一个波(或一个常数函数)分别映射到每个玩家,并预测下一轮的移动。从结果的一轮预测中,Meta Scientist选择看起来占多数的一方。

#include <iostream>
#include <utility>
#include <cstdlib>
#include <cstring>
#if 0
#define DBG(st) {st}
#else
#define DBG(st)
#endif

#define WINDOW (200)

using namespace std;

int main(int argc,char **argv){
    if(argc==1){
        cout<<(rand()%2?"good":"evil")<<endl;
        return 0;
    }
    DBG(cerr<<"WINDOW="<<WINDOW<<endl;)
    int nump,numr;
    nump=strchr(argv[1],',')-argv[1];
    numr=(strlen(argv[1])+1)/(nump+1);
    int period,r,p;
    int score,*scores=new int[WINDOW];
    int max; //some score will always get above 0, because if some score<0, the inverted wave will be >0.
    int phase,phasemax;
    int predicted=0; //The predicted number of goods for the next round
    int fromround=numr-WINDOW;
    if(fromround<0)fromround=0;
    pair<int,int> maxat; //period, phase
    DBG(cerr<<"Players:"<<endl;)
    for(p=0;p<nump;p++){
        DBG(cerr<<" p"<<p<<": ";)
        for(r=fromround;r<numr;r++)if(argv[1][r*(nump+1)+p]!=argv[1][p])break;
        if(r==numr){
            DBG(cerr<<"All equal! prediction="<<argv[1][p]<<endl;)
            predicted+=argv[1][(numr-1)*(nump+1)+p]-'0';
            continue;
        }
        max=0;
        maxat={-1,-1};
        for(period=1;period<=WINDOW;period++){
            scores[period-1]=0;
            phasemax=-1;
            for(phase=0;phase<2*period;phase++){
                score=0;
                for(r=fromround;r<numr;r++){
                    if(argv[1][r*(nump+1)+p]-'0'==1-(r+phase)%(2*period)/period)score++;
                    else score--;
                }
                if(score>scores[period-1]){
                    scores[period-1]=score;
                    phasemax=phase;
                }
            }
            if(scores[period-1]>max){
                max=scores[period-1];
                maxat.first=period;
                maxat.second=phasemax;
            }
            DBG(cerr<<scores[period-1]<<" ";)
        }
        DBG(cerr<<"(max="<<max<<" at {"<<maxat.first<<","<<maxat.second<<"})"<<endl;)
        DBG(cerr<<"     prediction: 1-("<<numr<<"+"<<maxat.second<<")%(2*"<<maxat.first<<")/"<<maxat.first<<"="<<(1-(numr+maxat.second)%(2*maxat.first)/maxat.first)<<endl;)
        predicted+=(1-(numr+maxat.second)%(2*maxat.first)/maxat.first);
    }
    DBG(cerr<<"Predicted outcome: "<<predicted<<" good + "<<(nump-predicted)<<" evil"<<endl;)
    if(predicted>nump/2)cout<<"evil"<<endl; //pick minority
    else cout<<"good"<<endl;
    delete[] scores;
    return 0;
}

如果要打开调试语句,请将行读数更改#if 0#if 1

进行编译g++ -O3 -std=c++0x -o MetaScientist MetaScientist.cpp(不需要警告,因此不需要-Wall)并运行MetaScientist.exe(当然可以包括参数)。如果您的要求很好,我可以为您提供Windows可执行文件。

编辑:显然,以前的版本用完了大约600轮游戏时间。这不应该那样做。它的时间消耗由#define WINDOW (...)生产线控制,更多的速度较慢,但​​往后看。


2
我谦虚地建议您尝试选择失败的一方。如果您能够一贯正确地猜测,那么您每回合将获得3分以上。
凯文

1
@Kevin是的,但是我认为它可能很快就猜错了一方,并且您需要连续七次正确地猜测失败的一方,以取得比始终保持多数正确的进步。我可能会改变它。
tomsmeding

1
@Kevin另外,正如Rusher在OP的评论中指出的那样,我首先想看看当Rusher在本周末为我们获得一个计分板时,它们的工作方式(科学家和元科学家)。拉舍尔(Rusher),对不起,但我懒得自己编辑所有内容... :)
弄破了

3
别担心!无论如何,运行这些命令可能并不安全。只要让我用Internet上50个陌生人编写的代码来破坏我的机器即可。
Rainbolt 2014年

1
@Kevin但是,这是如此MANY!我确实可以,但是我不喜欢它。我会看看这些票价如何。
tomsmeding

26

天使

最纯粹的球员。

程序

print "good"

命令

python Angel.py

22
Python是一门好语言。天使应该使用它似乎是很自然的。
jpmc26 2014年

23
我想提醒人们Python是蛇。一条蛇。
李斯特先生,2014年

3
@MrLister我想提醒你路西法是上帝把他逐出天堂之前的伟大天使吗?
Zibbobz

1
@Zibbobz是的...真可惜,他们摔倒了。他们本可以一起取得很多成就。
李斯特先生,2014年

24

阿耳emi弥斯·福尔(Artemis Fowl)

package Humans;

public class ArtemisFowl extends Human {
    public final String takeSides(String history) {
        int good = 0, evil = 0;
        for(int i = 0; i < history.length(); i++)   {
            switch(history.charAt(i))   {
                case '0': evil++; break;
                case '1': good++; break;
            }
        }
        if(good % 5 == 0){
           return "good";
        } else if (evil % 5 == 0){
           return "evil";
        } else {
           if(good > evil){
              return "good";
           } else if(evil > good){
              return "evil";
           } else {
              return Math.random() >= 0.5 ? "good" : "evil";
           }
        }
    }
}

在第7 著作《亚特兰蒂斯情结》中阿耳emi弥斯·福尔患上了一种心理疾病(称为“亚特兰蒂斯情结”),迫使他以5的倍数来做所有事情(说话,行动等)。当他不能以5的倍数完成该操作时,他会感到恐慌。我基本上是这样做的:看看善恶(故意偏见)是否可以被5除尽,如果都不能,那么我会惊慌&看看哪个更大或更大,然后再恐慌&随机选择。


4
当我读初中的Artemis Fowl时,只有两本书。很高兴看到现在有七个,迪斯尼正在将它制作成电影。
Rainbolt 2014年

1
实际上有8本书。
Kyle Kanos 2014年

7
越多越快乐(除非您正在阅读《时间之轮》)
Rainbolt 2014年

1
而你忘记break;switch
johnchen902

1
@ johnchen902,@ Manu:我在Java方面不是很有经验(我使用Fortran90 +并且仅在这里看到Java),因此出现了我的错误。我一个小时后到办公室要修理。
凯尔·坎诺斯

19

排毒

奇数令人恐惧。

package Humans;

public class Disparnumerophobic extends Human {
    public final String takeSides(String history) {
        int good = 0, evil = 0;
        for(int i = 0; i < history.length(); i++)   {
            switch(history.charAt(i))   {
                case '0': evil++; break;
                case '1': good++;
            }
        }
        if(good%2 == 1 && evil%2 == 0)  return "evil";
        if(evil%2 == 1 && good%2 == 0)  return "good";
        // well shit.... 
        return Math.random() >= 0.5 ? "good" : "evil";
    }
}

17
评论使我发笑/ s。
phyrfox 2014年

17

莱纳斯,露比

试图通过总是打破格局来使分析师感到困惑。

num_rounds = ARGV[0].to_s.count(',')
LINUS_SEQ = 0xcb13b2d3734ecb4dc8cb134b232c4d3b2dcd3b2d3734ec4d2c8cb134b234dcd3b2d3734ec4d2c8cb134b23734ecb4dcd3b2c4d232c4d2c8cb13b2d3734ecb4dcb232c4d2c8cb13b2d3734ecb4dc8cb134b232c4d3b2dcd3b2d3734ec4d2c8cb134b234dcd3b2d3734ec4d2c8cb134b23734ecb4dcd3b2c4d2c8cb134b2
puts %w[good evil][LINUS_SEQ[num_rounds]]

另存为linus.rb并运行ruby linus.rb


16

背包客

确定最选择匹配少数派并选择其最后一票的玩家。

package Humans;

public class BackPacker extends Human {
    // toggles weather the BackPacker thinks majority is better vs. minority is better
    private static final boolean goWithMajority = false;

    @Override
    public final String takeSides(String history)  {
        if (history == null || history.equals(""))
            return "evil";
        String[] roundVotes = history.split(",");
        int players = roundVotes[0].length();
        int[] winningPlayers = new int[players];
        for (String nextRound : roundVotes) {
            boolean didGoodWin = didGoodWin(nextRound, players);
            for (int player = 0; player < nextRound.length(); player++) {
                boolean playerVotedGood = nextRound.charAt(player) == '1';
                winningPlayers[player] += didPlayerWin(didGoodWin, playerVotedGood);
            }
        }
        int bestScore = -1;
        for (int nextPlayer : winningPlayers)
            if (bestScore < nextPlayer)
                bestScore = nextPlayer;
        int bestPlayer = 0;
        for (int ii = 0; ii < players; ii++) {
            if (winningPlayers[ii] == bestScore) {
                bestPlayer = ii;
                break;
            }
        }
        if (roundVotes[roundVotes.length - 1].charAt(bestPlayer) == '1')
            return "good";
        return "evil";
    }

    private int didPlayerWin(boolean didGoodWin, boolean playerVotedGood) {
        if(goWithMajority) {
            return ((didGoodWin && playerVotedGood) || (!didGoodWin && !playerVotedGood)) ? 1 : 0;
        } else {
            return ((!didGoodWin && playerVotedGood) || (didGoodWin && !playerVotedGood)) ? 1 : 0;
        }
    }

    private boolean didGoodWin(String round, int players) {
        int good = 0;
        for (char next : round.toCharArray())
            good += next == '1' ? 1 : 0;
        return (good * 2) > players;
    }
}

人群跟随者

确定选择了最多多数票并选择其最后一票的球员。

package Humans;

public class CrowdFollower extends Human {
    // toggles weather the FrontPacker thinks majority is better vs. minority is better
    private static final boolean goWithMajority = true;

    @Override
    public final String takeSides(String history)  {
        if (history == null || history.equals(""))
            return "evil";
        String[] roundVotes = history.split(",");
        int players = roundVotes[0].length();
        int[] winningPlayers = new int[players];
        for (String nextRound : roundVotes) {
            boolean didGoodWin = didGoodWin(nextRound, players);
            for (int player = 0; player < nextRound.length(); player++) {
                boolean playerVotedGood = nextRound.charAt(player) == '1';
                winningPlayers[player] += didPlayerWin(didGoodWin, playerVotedGood);
            }
        }
        int bestScore = -1;
        for (int nextPlayer : winningPlayers)
            if (bestScore < nextPlayer)
                bestScore = nextPlayer;
        int bestPlayer = 0;
        for (int ii = 0; ii < players; ii++) {
            if (winningPlayers[ii] == bestScore) {
                bestPlayer = ii;
                break;
            }
        }
        if (roundVotes[roundVotes.length - 1].charAt(bestPlayer) == '1')
            return "good";
        return "evil";
    }

    private int didPlayerWin(boolean didGoodWin, boolean playerVotedGood) {
        if(goWithMajority) {
            return ((didGoodWin && playerVotedGood) || (!didGoodWin && !playerVotedGood)) ? 1 : 0;
        } else playerVotedGood                return ((!didGoodWin && good) || (didGoodWin && !playerVotedGood)) ? 1 : 0;
        }
    }

    private boolean didGoodWin(String round, int players) {
        int good = 0;
        for (char next : round.toCharArray())
            good += next == '1' ? 1 : 0;
        return (good * 2) > players;
    }
}

很干净的程序!
Rainbolt 2014年

糟糕,我想我可能已经用其他语言复制了您的程序。
PyRulez 2014年

@Rusher我更新的代码,并想补充这两个项目,其中一个goWithMajority = true和一个地方的false。可以吗,或者我需要为此添加第二个BackPacker吗?
Angelo Fuchs 2014年

@AngeloNeuschitzer我编辑了这篇文章。这样,我就不会忘记添加两个提交。我建议您更改我给它起的非真实的名称,如果需要,可以在两者中都添加说明。
Rainbolt 2014年

1
@Rainbolt实际上,我更喜欢您的FrontPacker。大声笑。
tomsmeding

15

算命先生

这项工作仍在进行中。我还没有测试。我只是想看看OP是否认为它违反了规则。

这个想法是通过执行所有其他参与者几次来模拟下一轮,以获得结果的可能性并采取相应的行动。

package Humans;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import sun.net.www.protocol.file.FileURLConnection;

public class FortuneTeller extends Human {

/**
 * Code from http://stackoverflow.com/a/22462785 Private helper method
 *
 * @param directory The directory to start with
 * @param pckgname The package name to search for. Will be needed for
 * getting the Class object.
 * @param classes if a file isn't loaded but still is in the directory
 * @throws ClassNotFoundException
 */
private static void checkDirectory(File directory, String pckgname,
        ArrayList<Class<?>> classes) throws ClassNotFoundException {
    File tmpDirectory;

    if (directory.exists() && directory.isDirectory()) {
        final String[] files = directory.list();

        for (final String file : files) {
            if (file.endsWith(".class")) {
                try {
                    classes.add(Class.forName(pckgname + '.'
                            + file.substring(0, file.length() - 6)));
                } catch (final NoClassDefFoundError e) {
                // do nothing. this class hasn't been found by the
                    // loader, and we don't care.
                }
            } else if ((tmpDirectory = new File(directory, file))
                    .isDirectory()) {
                checkDirectory(tmpDirectory, pckgname + "." + file, classes);
            }
        }
    }
}

/**
 * Private helper method.
 *
 * @param connection the connection to the jar
 * @param pckgname the package name to search for
 * @param classes the current ArrayList of all classes. This method will
 * simply add new classes.
 * @throws ClassNotFoundException if a file isn't loaded but still is in the
 * jar file
 * @throws IOException if it can't correctly read from the jar file.
 */
private static void checkJarFile(JarURLConnection connection,
        String pckgname, ArrayList<Class<?>> classes)
        throws ClassNotFoundException, IOException {
    final JarFile jarFile = connection.getJarFile();
    final Enumeration<JarEntry> entries = jarFile.entries();
    String name;

    for (JarEntry jarEntry = null; entries.hasMoreElements()
            && ((jarEntry = entries.nextElement()) != null);) {
        name = jarEntry.getName();

        if (name.contains(".class")) {
            name = name.substring(0, name.length() - 6).replace('/', '.');

            if (name.contains(pckgname)) {
                classes.add(Class.forName(name));
            }
        }
    }
}

/**
 * Attempts to list all the classes in the specified package as determined
 * by the context class loader
 *
 * @param pckgname the package name to search
 * @return a list of classes that exist within that package
 * @throws ClassNotFoundException if something went wrong
 */
private static ArrayList<Class<?>> getClassesForPackage(String pckgname)
        throws ClassNotFoundException {
    final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();

    try {
        final ClassLoader cld = Thread.currentThread()
                .getContextClassLoader();

        if (cld == null) {
            throw new ClassNotFoundException("Can't get class loader.");
        }

        final Enumeration<URL> resources = cld.getResources(pckgname
                .replace('.', '/'));
        URLConnection connection;

        for (URL url = null; resources.hasMoreElements()
                && ((url = resources.nextElement()) != null);) {
            try {
                connection = url.openConnection();

                if (connection instanceof JarURLConnection) {
                    checkJarFile((JarURLConnection) connection, pckgname,
                            classes);
                } else if (connection instanceof FileURLConnection) {
                    try {
                        checkDirectory(
                                new File(URLDecoder.decode(url.getPath(),
                                                "UTF-8")), pckgname, classes);
                    } catch (final UnsupportedEncodingException ex) {
                        throw new ClassNotFoundException(
                                pckgname
                                + " does not appear to be a valid package (Unsupported encoding)",
                                ex);
                    }
                } else {
                    throw new ClassNotFoundException(pckgname + " ("
                            + url.getPath()
                            + ") does not appear to be a valid package");
                }
            } catch (final IOException ioex) {
                throw new ClassNotFoundException(
                        "IOException was thrown when trying to get all resources for "
                        + pckgname, ioex);
            }
        }
    } catch (final NullPointerException ex) {
        throw new ClassNotFoundException(
                pckgname
                + " does not appear to be a valid package (Null pointer exception)",
                ex);
    } catch (final IOException ioex) {
        throw new ClassNotFoundException(
                "IOException was thrown when trying to get all resources for "
                + pckgname, ioex);
    }

    return classes;
}

private static boolean isRecursiveCall = false;
private static ArrayList<Class<?>> classes;

static {
    if (classes == null) {
        try {
            classes = getClassesForPackage("Humans");
        } catch (ClassNotFoundException ex) {

        }
    }    
}

private String doThePetyrBaelish() {
    return Math.random() >= 0.5 ? "good" : "evil";
}

@Override
public String takeSides(String history) {
    if (isRecursiveCall) {
        return doThePetyrBaelish();
    }
    isRecursiveCall = true;

    int currentRoundGoodCount = 0;
    float probabilityOfGood = 0;
    int roundCount = 0;
    int voteCount = 0;



    do {
        for (int i = 0; i < classes.size(); i++) {
            try {
                if (classes.get(i).getName() == "Humans.FortuneTeller") {
                    continue;
                }

                Human human = (Human) classes.get(i).newInstance();
                String response = human.takeSides(history);
                switch (response) {
                    case "good":
                        currentRoundGoodCount++;
                        voteCount++;
                        break;
                    case "evil":
                        voteCount++;
                        break;
                    default:
                        break;
                }
            } catch (Exception e) {
            }
        }

        probabilityOfGood = (probabilityOfGood * roundCount
                + (float) currentRoundGoodCount / voteCount) / (roundCount + 1);

        roundCount++;
        currentRoundGoodCount = 0;
        voteCount = 0;

    } while (roundCount < 11);

    isRecursiveCall = false;
    if (probabilityOfGood > .7) {
        return "evil";
    }
    if (probabilityOfGood < .3) {
        return "good";
    }

    return doThePetyrBaelish();
}

}

如果您的机器人在回答之前每回合都运行着其他所有机器人,那么回答时间是否会超过1秒?
plannapus 2014年

@plannapus我猜这个机器人的假设是,其他所有人都将谨慎行事,避免任何值得一秒钟的等待。我认为值得一提的是,提交并输入一个0.9秒的等待时间,然后返回“ good”(好),以免惹他。实际上,SBoss击败了我:D
scragar's Julgar

耶!然后,我必须在代码中将该机器人列入黑名单。这将令人沮丧。此外,在不同环境(例如Python或Perl)中使用不同的条目时,重新加载解释器可能仅足以使此代码超过时间限制。
Andris 2014年

16
如果其他人做与此相同的事情,则会出现无限循环。
Brilliand 2014年

4
提交超时。我安装了一个探查器,花了将近半秒的时间来调用一些提交文件。至少它可以工作,对此表示祝贺。
Rainbolt

15

C ++,科学家

这一次尝试,用什么样的多数在每轮选择的历史wavemajority()给出圆多数人的选择),适合波数据,波长2*period和相位phase。因此,给定0,1,1,1,0,1,0,1,1,1,0,0,0,1,0它选择period=3, phase=5maxat=={3,5}):其得分变为9 3 11 5 5 3 5 7 9 7 7 7 7 7 7。它遍历所有可能的时间段,并且如果该时间段的分数高于当前最大值,则存储{period,phase}发生该事件的分数。

然后将找到的波浪外推到下一轮,并获得预测的多数。

#include <iostream>
#include <utility>
#include <cstdlib>
#include <cstring>
#if 0
#define DBG(st) {st}
#else
#define DBG(st)
#endif

#define WINDOW (700)

using namespace std;

int majority(const char *r){
    int p=0,a=0,b=0;
    while(true){
        if(r[p]=='1')a++;
        else if(r[p]=='0')b++;
        else break;
        p++;
    }
    return a>b;
}

int main(int argc,char **argv){
    if(argc==1){
        cout<<(rand()%2?"good":"evil")<<endl;
        return 0;
    }
    DBG(cerr<<"WINDOW="<<WINDOW<<endl;)
    int nump,numr;
    nump=strchr(argv[1],',')-argv[1];
    numr=(strlen(argv[1])+1)/(nump+1);
    int fromround=numr-30;
    if(fromround<0)fromround=0;
    int period,r;
    int *wave=new int[WINDOW];
    bool allequal=true;
    DBG(cerr<<"wave: ";)
    for(r=fromround;r<numr;r++){
        wave[r-fromround]=majority(argv[1]+r*(nump+1));
        if(wave[r-fromround]!=wave[0])allequal=false;
        DBG(cerr<<wave[r]<<" ";)
    }
    DBG(cerr<<endl;)
    if(allequal){
        DBG(cerr<<"All equal!"<<endl;)
        if(wave[numr-1]==1)cout<<"evil"<<endl; //choose for minority
        else cout<<"good"<<endl;
        return 0;
    }
    int score,*scores=new int[WINDOW];
    int max=0; //some score will always get above 0, because if some score<0, the inverted wave will be >0.
    int phase,phasemax;
    pair<int,int> maxat(-1,-1); //period, phase
    DBG(cerr<<"scores: ";)
    for(period=1;period<=WINDOW;period++){
        scores[period-1]=0;
        phasemax=-1;
        for(phase=0;phase<2*period;phase++){
            score=0;
            for(r=fromround;r<numr;r++){
                if(wave[r]==1-(r+phase)%(2*period)/period)score++;
                else score--;
            }
            if(score>scores[period-1]){
                scores[period-1]=score;
                phasemax=phase;
            }
        }
        if(scores[period-1]>max){
            max=scores[period-1];
            maxat.first=period;
            maxat.second=phasemax;
        }
        DBG(cerr<<scores[period-1]<<" ";)
    }
    DBG(cerr<<"(max="<<max<<" at {"<<maxat.first<<","<<maxat.second<<"})"<<endl;)
    DBG(cerr<<" max: ("<<numr<<"+"<<maxat.second<<")%(2*"<<maxat.first<<")/"<<maxat.first<<"=="<<((numr+maxat.second)%(2*maxat.first)/maxat.first)<<endl;)
    if(1-(numr+maxat.second)%(2*maxat.first)/maxat.first==1)cout<<"evil"<<endl; //choose for minority
    else cout<<"good"<<endl;
    delete[] wave;
    delete[] scores;
    return 0;
}

进行编译g++ -O3 -std=c++0x -o Scientist Scientist.cpp(不需要警告,因此不需要-Wall)并运行Scientist.exe(当然可以包括参数)。如果您的要求很好,我可以为您提供Windows可执行文件。

哦,不要害怕弄乱输入格式。否则会做奇怪的事情。

编辑:显然,以前的版本用完了大约600轮游戏时间。这不应该那样做。它的时间消耗由#define WINDOW (...)生产线控制,更多的速度较慢,但​​往后看。


8
在互联网上下载由六十多个陌生人编写的可执行文件似乎是个坏主意。
Rainbolt

@Rusher我完全同意。如果您确实想要问题,那就是“傻瓜”指南中的第一步。不过,我的报价仍然有效:)
表示

2
得到这个编译(和竞争)罚款。
Rainbolt 2014年

14

代码运行器

因此,为了使事情变得有趣,我创建了一个脚本来自动从每个发布的答案中下载代码,并在必要时进行编译,然后根据规则运行所有解决方案。这样,人们可以检查他们的状况。只需将此脚本保存到run_all.py(需要BeautifulSoup),然后:

usage:
To get the latest code: 'python run_all.py get'
To run the submissions: 'python run_all.py run <optional num_runs>'

一些东西:

  1. 如果要添加对更多语言的支持,或者要删除对某些语言的支持,请参阅def submission_type(lang)
  2. 扩展脚本应该相当容易,即使对于需要编译的语言(请参阅参考资料CPPSubmission)。语言类型是从meta code标记中获取的< !-- language: lang-java -- >,因此,如果要运行代码,请确保将其添加(删除<>前后的多余空格)。 更新:现在有一些非常基础的推论来尝试检测未定义的语言。
  3. 如果您的代码完全无法运行,或无法在指定的时间内完成,则它将被添加到blacklist.text将来的试用版中并从以后的试用版中自动删除。如果您修改了代码,只需从黑名单中删除您的条目,然后重新运行get

当前支持的语言:

 submission_types =  {
    'lang-ruby': RubySubmission,
    'lang-python': PythonSubmission,
    'lang-py': PythonSubmission,
    'lang-java': JavaSubmission,
    'lang-Java': JavaSubmission,
    'lang-javascript': NodeSubmission,
    'lang-cpp': CPPSubmission,
    'lang-c': CSubmission,
    'lang-lua': LuaSubmission,
    'lang-r': RSubmission,
    'lang-fortran': FortranSubmission,
    'lang-bash': BashSubmission
}

无需再费周折:

import urllib2
import hashlib
import os
import re
import subprocess
import shutil
import time
import multiprocessing
import tempfile
import sys
from bs4 import BeautifulSoup

__run_java__ = """
public class Run {
    public static void main(String[] args) {
        String input = "";
        Human h = new __REPLACE_ME__();
        if(args.length == 1)
            input = args[0];
        try {
            System.out.println(h.takeSides(input));
        }
        catch(Exception e) {
        }
    }
}
"""

__human_java__ = """
public abstract class Human {
    public abstract String takeSides(String history) throws Exception;
}
"""

class Submission():
    def __init__(self, name, code):
        self.name = name
        self.code = code

    def submissions_dir(self):
        return 'submission'

    def base_name(self):
        return 'run'

    def submission_path(self):
        return os.path.join(self.submissions_dir(), self.name)

    def extension(self):
        return ""

    def save_submission(self):
        self.save_code()

    def full_command(self, input):
        return []

    def full_path(self):
        file_name = "%s.%s" % (self.base_name(), self.extension())
        full_path = os.path.join(self.submission_path(), file_name)
        return full_path

    def save_code(self):    
        if not os.path.exists(self.submission_path()):
            os.makedirs(self.submission_path())

        with open(self.full_path(), 'w') as f:
            f.write(self.code)

    def write_err(self, err):
        with open(self.error_log(), 'w') as f:
            f.write(err)

    def error_log(self):
        return os.path.join(self.submission_path(), 'error.txt')

    def run_submission(self, input):
        command = self.full_command()
        if input is not None:
            command.append(input)
        try:
            output,err,exit_code = run(command,timeout=1)
            if len(err) > 0:
                self.write_err(err)
            return output
        except Exception as e:
            self.write_err(str(e))
            return ""

class CPPSubmission(Submission):
    def bin_path(self):
        return os.path.join(self.submission_path(), self.base_name())

    def save_submission(self):
        self.save_code()
        compile_cmd = ['g++', '-O3', '-std=c++0x', '-o', self.bin_path(), self.full_path()]
        errout = open(self.error_log(), 'w')
        subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)

    def extension(self):
        return 'cpp'

    def full_command(self):
        return [self.bin_path()]

class CSubmission(Submission):
    def bin_path(self):
        return os.path.join(self.submission_path(), self.base_name())

    def save_submission(self):
        self.save_code()
        compile_cmd = ['gcc', '-o', self.bin_path(), self.full_path()]
        errout = open(self.error_log(), 'w')
        subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)

    def extension(self):
        return 'c'

    def full_command(self):
        return [self.bin_path()]

class FortranSubmission(Submission):
    def bin_path(self):
        return os.path.join(self.submission_path(), self.base_name())

    def save_submission(self):
        self.save_code()
        compile_cmd = ['gfortran', '-fno-range-check', '-o', self.bin_path(), self.full_path()]
        errout = open(self.error_log(), 'w')
        subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)

    def extension(self):
        return 'f90'

    def full_command(self):
        return [self.bin_path()]

class JavaSubmission(Submission):   
    def base_name(self):
        class_name = re.search(r'class (\w+) extends', self.code)
        file_name = class_name.group(1)
        return file_name

    def human_base_name(self):
        return 'Human'

    def run_base_name(self):
        return 'Run'

    def full_name(self, base_name):
        return '%s.%s' % (base_name, self.extension())

    def human_path(self):
        return os.path.join(self.submission_path(), self.full_name(self.human_base_name()))

    def run_path(self):
        return os.path.join(self.submission_path(), self.full_name(self.run_base_name()))

    def replace_in_file(self, file_name, str_orig, str_new):
        old_data = open(file_name).read()
        new_data = old_data.replace(str_orig, str_new)

        with open(file_name, 'w') as f:
            f.write(new_data)

    def write_code_to_file(self, code_str, file_name):
        with open(file_name, 'w') as f:
            f.write(code_str)

    def save_submission(self):
        self.save_code()
        self.write_code_to_file(__human_java__, self.human_path())
        self.write_code_to_file(__run_java__, self.run_path())

        self.replace_in_file(self.run_path(), '__REPLACE_ME__', self.base_name())
        self.replace_in_file(self.full_path(), 'package Humans;', '')

        compile_cmd = ['javac', '-cp', self.submission_path(), self.run_path()]
        errout = open(self.error_log(), 'w')
        subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)

    def extension(self):
        return 'java'

    def full_command(self):
        return ['java', '-cp', self.submission_path(), self.run_base_name()]

class PythonSubmission(Submission):
    def full_command(self):
        return ['python', self.full_path()]

    def extension(self):
        return 'py'

class RubySubmission(Submission):
    def full_command(self):
        return ['ruby', self.full_path()]

    def extension(self):
        return 'rb'

class NodeSubmission(Submission):
    def full_command(self):
        return ['node', self.full_path()]

    def extension(self):
        return 'js'

class LuaSubmission(Submission):
    def full_command(self):
        return ['lua', self.full_path()]

    def extension(self):
        return 'lua'

class RSubmission(Submission):
    def full_command(self):
        return ['Rscript', self.full_path()]

    def extension(self):
        return 'R'

class BashSubmission(Submission):
    def full_command(self):
        return [self.full_path()]

    def extension(self):
        return '.sh'

class Scraper():
    def download_page(self, url, use_cache = True, force_cache_update = False):
        file_name = hashlib.sha1(url).hexdigest()

        if not os.path.exists('cache'):
            os.makedirs('cache')

        full_path = os.path.join('cache', file_name)
        file_exists = os.path.isfile(full_path)

        if use_cache and file_exists and not force_cache_update:
            html = open(full_path, 'r').read()
            return html

        opener = urllib2.build_opener()
        opener.addheaders = [('User-agent', 'Mozilla/5.0')]
        response = opener.open(url)
        html = response.read()

        if use_cache:
            f = open(full_path, 'w')
            f.write(html)
            f.close()

        return html

    def parse_post(self, post):
        name = post.find(text=lambda t: len(t.strip()) > 0)
        pre = post.find('pre')
        lang = pre.attrs['class'][0] if pre.has_attr('class') else None
        code = pre.find('code').text
        user = post.find(class_='user-details').find(text=True)
        return {'name':name,'lang':lang,'code':code,'user':user}

    def parse_posts(self, html):
        soup = BeautifulSoup(html)
        # Skip the first post
        posts = soup.find_all(class_ = 'answercell')
        return [self.parse_post(post) for post in posts]

    def get_submissions(self,  page = 1, force_cache_update = False):
        url = "http://codegolf.stackexchange.com/questions/33137/good-versus-evil?page=%i&tab=votes#tab-top" % page
        html = self.download_page(url, use_cache = True, force_cache_update = force_cache_update)
        submissions = self.parse_posts(html)
        return submissions

class Timeout(Exception):
    pass

def run(command, timeout=10):
    proc = subprocess.Popen(command, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    poll_seconds = .250
    deadline = time.time()+timeout
    while time.time() < deadline and proc.poll() == None:
        time.sleep(poll_seconds)

    if proc.poll() == None:
        if float(sys.version[:3]) >= 2.6:
            proc.terminate()
        raise Timeout()

    stdout, stderr = proc.communicate()
    return stdout, stderr, proc.returncode


def guess_lang(code):
    if re.search(r'class .* extends Human', code):
        return 'lang-java'
    if re.search(r'import sys', code):
        return 'lang-python'
    if re.search(r'puts', code) and (re.search(r'ARGV', code) or re.search(r'\%w', code)):
        return 'lang-ruby'
    if re.search(r'console\.log', code):
        return 'lang-javascript'
    if re.search(r'program', code) and re.search(r'subroutine', code):
        return 'lang-fortran'
    if re.search(r'@echo off', code):
        return 'lang-bash'
    return None


def submission_type(lang, code):
    submission_types =  {
        'lang-ruby': RubySubmission,
        'lang-python': PythonSubmission,
        'lang-py': PythonSubmission,
        'lang-java': JavaSubmission,
        'lang-Java': JavaSubmission,
        'lang-javascript': NodeSubmission,
        'lang-cpp': CPPSubmission,
        'lang-c': CSubmission,
        'lang-lua': LuaSubmission,
        'lang-r': RSubmission,
        'lang-fortran': FortranSubmission,
        'lang-bash': BashSubmission
    }

    klass = submission_types.get(lang)

    if klass is None:
        lang = guess_lang(code)
        klass = submission_types.get(lang)

    return klass

def instantiate(submission):
    lang = submission['lang']
    code = submission['code']
    name = submission['name']

    klass = submission_type(lang, code)
    if klass is not None:
        instance = klass(name, code)
        return instance
    print "Entry %s invalid - lang not supported: %s" % (name, lang)
    return None

def get_all_instances(force_update):
    scraper = Scraper()

    print 'Scraping Submissions..'

    pages = [1,2,3]
    submissions_by_page = [scraper.get_submissions(page=i, force_cache_update=force_update) for i in pages]
    submissions = [item for sublist in submissions_by_page for item in sublist]

    # Get instances
    raw_instances = [instantiate(s) for s in submissions]
    instances = [i for i in raw_instances if i]

    print "Using %i/%i Submissions" % (len(instances), len(submissions))

    return instances

def save_submissions(instances):
    print 'Saving Submissions..'

    for instance in instances:
        instance.save_submission()

def init_game(save=True, force_update=False):
    instances = get_all_instances(force_update)
    if save:
        save_submissions(instances)
    return instances

def one_run(instances, input):
    valid = {
        'good': 1,
        'evil': 0
    }

    disqualified = []
    results = []

    for instance in instances:
        out = instance.run_submission(input)
        res = out.strip().lower()
        if res not in valid:
            disqualified.append(instance)
        else:
            results.append(valid[res])

    return (results, disqualified)

def get_winner(scores, instances):
    max_value = max(scores)
    max_index = scores.index(max_value)
    instance = instances[max_index]
    return (instance.name, max_value)

def update_scores(results, scores, minority_counts, minority_num):
    for i in range(len(results)):
        if results[i] == minority_num:
            minority_counts[i] += 1
            scores[i] += (minority_counts[i] - 1)
        else:
            minority_counts[i] = 0
            scores[i] += 3

def try_run_game(instances, num_runs = 1000, blacklist = None):
    current_input = None
    minority_str = None
    num_instances = len(instances)
    scores = [0] * num_instances
    minority_counts = [0] * num_instances

    print "Running with %i instances..." % num_instances

    for i in range(num_runs):
        print "Round: %i - Last minority was %s" % (i, minority_str)
        results, disqualified = one_run(instances, current_input)

        if len(disqualified) > 0:
            for instance in disqualified:
                print "Removing %s!" % instance.name
                instances.remove(instance)

                if blacklist is not None:
                    with open(blacklist, 'a') as f:
                        f.write("%s\n" % instance.name)

            return False

        latest_result = "".join(map(str,results))
        current_input = "%s,%s" % (current_input, latest_result)

        minority_num = 1 if results.count(1) < results.count(0) else 0
        minority_str = 'good' if minority_num == 1 else 'evil'

        update_scores(results, scores, minority_counts, minority_num)
        name, score = get_winner(scores, instances)
        print "%s is currently winning with a score of %i" % (name, score)

    print "The winner is %s with a score of %i!!!" % (name, score)
    return True

def find_instance_by_name(instances, name):
    for instance in instances:
        if instance.name == name:
            return instance
    return None

def maybe_add_or_remove_baelish(instances, baelish):
    num_instances = len(instances)

    if num_instances % 2 == 0:
        print 'There are %i instances.' % num_instances
        try:
            instances.remove(baelish)
            print 'Baelish Removed!'
        except:
            instances.append(baelish)
            print 'Baelish Added!'

def remove_blacklisted(blacklist, instances):
    blacklisted = []

    try:
        blacklisted = open(blacklist).readlines()
    except:
        return

    print 'Removing blacklisted entries...'

    for name in blacklisted:
        name = name.strip()
        instance = find_instance_by_name(instances, name)
        if instance is not None:
            print 'Removing %s' % name
            instances.remove(instance)

def run_game(instances, num_runs):
    blacklist = 'blacklist.txt'
    remove_blacklisted(blacklist, instances)

    baelish = find_instance_by_name(instances, 'Petyr Baelish') 
    maybe_add_or_remove_baelish(instances, baelish)

    while not try_run_game(instances, num_runs = num_runs, blacklist = blacklist):
        print "Restarting!"
        maybe_add_or_remove_baelish(instances, baelish)

    print "Done!"

if __name__ == '__main__':
    param = sys.argv[1] if len(sys.argv) >= 2 else None

    if param == 'get':
        instances = init_game(save=True, force_update=True)
    elif param == 'run':
        instances = init_game(save=False, force_update=False)
        num_runs = 50
        if len(sys.argv) == 3:
            num_runs = int(sys.argv[2])
        run_game(instances, num_runs)
    else:
        self_name = os.path.basename(__file__)
        print "usage:"
        print "To get the latest code: 'python %s get'" % self_name
        print "To run the submissions: 'python %s run <optional num_runs>'" % self_name

为什么没有Fortran语言
凯尔·坎诺斯

@KyleKanos-我添加了对此的支持,将很快更新代码。
WhatAWorld

好极了!我(sorta)在我的Fortran提交上努力工作,Rusher无法使其正常工作,所以我希望有人得到它:)
Kyle Kanos

1
@Rusher:我同意PeterTaylor的这一观点:语法高亮显示应被拒绝,因为唯一建议的编辑应被拒绝。编辑应用于实质性更正,而不是次要的内容。
凯尔·坎诺斯

1
您确实值得为此提供代表,但是由于这并不是问题的答案(并且可能会从社区添加其他语言的内容中受益),因此我认为从技术上讲,这应该是社区Wiki。
Martin Ender 2014年

13

美丽的心灵,Ruby

根据上一轮的位表示中有意义的模式进行决策

require 'prime'

if ARGV.length == 0
    puts ["good", "evil"].sample
else
    last_round = ARGV[0].split(',').last
    puts Prime.prime?(last_round.to_i(2)) ? "good" : "evil"
end

运行像

ruby beautiful-mind.rb

13

Lua,多情

相信神迹奇事的迷信程序。

history = arg[1]

if history == nil then
    print("good")
else
    local EvilSigns, GoodSigns = 0,0
    local SoulSpace = ""

    for i in string.gmatch(history, "%d+") do
         SoulSpace = SoulSpace .. i 
    end

    if string.match(SoulSpace, "1010011010")  then -- THE NUBMER OF THE BEAST!
        local r = math.random(1000)
        if r <= 666 then print("evil") else print("good") end
    else
        for i in string.gmatch(SoulSpace, "10100") do -- "I'M COMING" - DEVIL
            EvilSigns = EvilSigns + 1
        end
        for i in string.gmatch(SoulSpace, "11010") do -- "ALL IS WELL" - GOD
            GoodSigns = GoodSigns + 1
        end

        if EvilSigns > GoodSigns then 
            print("evil")
        elseif GoodSigns > EvilSigns then
            print("good")
        elseif GoodSigns == EvilSigns then
            local r = math.random(1000)
            if r <= 666 then print("good") else print("evil") end
        end
    end
end

使用以下命令运行它:

lua Piustitious.lua

其次是输入。


11

温彻斯特

Sam和Dean很好(大部分时间)。

package Humans;

public class TheWinchesters extends Human {

    @Override
    public String takeSides(String history) throws Exception {
        return Math.random() < 0.1 ? "evil" : "good";
    }

}

您确定9:1正确的比率吗?也许我们应该进行一些数据挖掘并获得更精确的比率?
recursion.ninja 2014年

1
@awashburn我2个月前开始看《超自然》(现在停留在第9季),9:1对我来说似乎还好;)
CommonGuy 2014年

10

统计员

public class Statistician extends Human{
    public final String takeSides(String history) { 
        int side = 0;
        String[] hist = history.split(",");
        for(int i=0;i<hist.length;i++){
            for(char c:hist[i].toCharArray()){
                side += c == '1' ? (i + 1) : -(i + 1);
            }
        }
        if(side == 0) side += Math.round(Math.random());
        return side > 0 ? "good" : "evil";
    }
}

5
最后第二行太棒了
cjfaure 2014年

5
@Undeserved代替Math.ceil(Math.random()-Math.random())您也可以做Math.round(Math.random())
tomsmeding

10

R,有点像贝叶斯机器人

将每个用户的频率表用作其他用户输出的先验概率。

args <- commandArgs(TRUE)
if(length(args)!=0){
    history <- do.call(rbind,strsplit(args,","))
    history <- do.call(rbind,strsplit(history,""))
    tabulated <- apply(history,2,function(x)table(factor(x,0:1)))
    result <- names(which.max(table(apply(tabulated, 2, function(x)sample(0:1,1, prob=x)))))
    if(result=="1"){cat("good")}else{cat("evil")}
}else{
    cat("good")
    }

Rscript BayesianBot.R在输入之后使用和调用。

编辑:只是为了澄清这是在做什么,这是示例输入的逐步说明:

> args
[1] "11011,00101,11101,11111,00001,11001,11001"
> history #Each player is a column, each round a row
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    1    0    1    1
[2,]    0    0    1    0    1
[3,]    1    1    1    0    1
[4,]    1    1    1    1    1
[5,]    0    0    0    0    1
[6,]    1    1    0    0    1
[7,]    1    1    0    0    1

> tabulated #Tally of each player previous decisions.
  [,1] [,2] [,3] [,4] [,5]
0    2    2    4    5    0
1    5    5    3    2    7

然后,以开头的那一行result<-对于每个玩家,使用最后一张表作为权重随机地选择0或1(即,对于玩家1,选择0的概率为2/7,选择1的概率为5/7,依此类推)。它为每个玩家/列选择一个结果,最后返回以最常见的数字结束的数字。


10

瑞士人

始终保持中立。注定永远不会赢。

package Humans;

/**
 * Never choosing a side, sustaining neutrality
 * @author Fabian
 */
public class Swiss extends Human {   
    public String takeSides(String history) {
        return "neutral"; // wtf, how boring is that?
    }
}

我没有写这个!
Rainbolt 2014年

讽刺的是。中立永远不会胜利
fabigler 2014年

2
@Rusher啊,我明白了:D
fabigler 2014年

1
它甚至不编译-缺少分号。
圣保罗Ebermann

9

哈尔9000

#!/usr/bin/env perl
print eval("evil")

编辑:也许这更适合HAL 9000,但是要小心!这是非常邪恶的。我建议cd在运行目录之前先清空目录。

#!/usr/bin/env perl
print eval {
    ($_) = grep { -f and !/$0$/ } glob('./*');
    unlink;
    evil
}

这样cwd每次调用都会删除一个文件!

不太明显的调用:

以M $

D:\>copy con hal_9000.pl
#!/usr/bin/env perl
print eval("evil")
^Z
        1 file(s) copied.

D:>hal_9000.pl
evil

在* nix中

[core1024@testing_pc ~]$ tee hal_9000.pl
#!/usr/bin/env perl
print eval("evil")
# Press C-D here
[core1024@testing_pc ~]$ chmod +x $_
[core1024@testing_pc ~]$ ./$_
evil[core1024@testing_pc ~]$

您需要提供一个可用于运行程序的命令。有关更多信息,请参见挑战的“可交付成果”部分。
Rainbolt 2014年

@Rusher Done;)
core1024 2014年

9

多数意志

import sys
import random

if len(sys.argv)==1:
    print(random.choice(['good','evil']))
else:
    rounds=sys.argv[1].split(',')
    last_round=rounds[-1]
    zeroes=last_round.count('0')
    ones=last_round.count('1')
    if ones>zeroes:
        print('good')
    elif zeroes>ones:
        print('evil')
    elif ones==zeroes:
        print(random.choice(['good','evil']))

将其另存为WotM.pypython3 WotM.py然后按输入运行。

一个简单的程序,只是看它会如何工作。多数人上次所说的话都是这样,否则是随机的。


您需要提供一个可用于运行程序的命令。有关更多信息,请参见挑战的“可交付成果”部分。
Rainbolt 2014年

该死的,这使我的重复了。:D将我的改为少数。
Martin Ender 2014年

@Rusher添加了命令。那是你要找的东西吗?
isaacg 2014年

@isaacg完美!
Rainbolt 2014年

1
我根据计分板上的分数计算了平均排名,而该条目以该方式获胜。
Brilliand

9

艾伦·希勒

重复他刚才坐在旁边的人说的任何话。如果该人发现自己错了,那么他会转到下一个人,然后重复他们说的话。

package Humans;

/**
 * Alan Shearer copies someone whilst they're right; if they get predict
 * wrongly then he moves to the next person and copies whatever they say.
 *
 * @author Algy
 * @url http://codegolf.stackexchange.com/questions/33137/good-versus-evil
 */
public class AlanShearer extends Human {

    private char calculateWinner(String round) {
        int good = 0, evil = 0;

        for (int i = 0, L = round.length(); i < L; i++) {
            if (round.charAt(i) == '1') {
                good++;
            } else {
                evil++;
            }
        }

        return (good >= evil) ? '1' : '0';
    }

    /**
     * Take the side of good or evil.
     * @param history The past votes of every player
     * @return A String "good" or "evil
     */
    public String takeSides(String history) {
        String[] parts = history.split(",");
        String lastRound = parts[parts.length() - 1];

        if (parts.length() == 0 || lastRound.length() == 0) {
            return "good";
        } else {
            if (parts.length() == 1) {
                return lastRound.charAt(0) == '1' ? "good" : "evil";
            } else {
                int personToCopy = 0;

                for (int i = 0, L = parts.length(); i < L; i++) {
                    if (parts[i].charAt(personToCopy) != calculateWinner(parts[i])) {
                        personToCopy++;

                        if (personToCopy >= L) {
                            personToCopy = 0;
                        }
                    }
                }
            }

            return lastRound.charAt(personToCopy) == '1' ? "good" : "evil";
        }
    }
}

lastRound甚至在声明变量之前就引用了一个变量。另外,您在所有括号中都加上了括号,String.length但这不是函数。您可以将提交的内容进行编译吗?
Rainbolt 2014年

@Rusher-完成:)
Algy Taylor

@Algy:lastRound.length仍在lastRound声明之前访问(在第一个if中)(在if中是else)。请在此处提交之前尝试编译(或运行)您的代码。
圣保罗Ebermann

@PaŭloEbermann -道歉,我不是在一个环境下,我可以运行它-修订提出,虽然
阿尔杰·泰勒

现在,当超出范围时,您将引用一个名为“ personToCopy”的变量。我只是将其移到else块内,以便对其进行编译,但是我不知道这是否是您想要的。
Rainbolt 2014年

8

后来是Evil,JavaScript(node.js

测量两次执行之间的时间。如果时差大于上次,则一定是邪恶的。否则,很好。

var fs = require('fs'),
currentTime = (new Date).getTime();

try {
    var data = fs.readFileSync('./laterisevil.txt', 'utf8');
} catch (e) { data = '0 0'; } // no file? no problem, let's start out evil at epoch

var parsed = data.match(/(\d+) (\d+)/),
lastTime = +parsed[1],
lastDifference = +parsed[2],
currentDifference = currentTime - lastTime;

fs.writeFileSync('./laterisevil.txt', currentTime + ' ' + currentDifference, 'utf8');
console.log(currentDifference > lastDifference? 'evil' : 'good');

运行: node laterisevil.js


8

模式查找器,Python

寻找一个重复出现的模式,如果找不到,就选择大多数。

import sys

if len(sys.argv) == 1: 
    print('good')
    quit()

wins = ''.join(
    map(lambda s: str(int(s.count('1') > s.count('0'))),
        sys.argv[1].split(',')
    )
)

# look for a repeating pattern
accuracy = []

for n in range(1, len(wins)//2+1):
    predicted = wins[:n]*(len(wins)//n)
    actual    = wins[:len(predicted)]
    n_right = 0
    for p, a in zip(predicted, actual):
        n_right += (p == a)
    accuracy.append(n_right/len(predicted))

# if there's a good repeating pattern, use it
if accuracy:
    best = max(accuracy)
    if best > 0.8:
        n = accuracy.index(best)+1
        prediction = wins[:n][(len(wins))%n]
        # good chance of success by going with minority
        if prediction == '1':
            print('evil')
        else:
            print('good')
        quit()

# if there's no good pattern, just go with the majority
if wins.count('1') > wins.count('0'):
    print('good')
else:
    print('evil')

python3 pattern_finder.py

1
我非常喜欢这段代码,当我运行它时,总会以某种方式获得3000点。
Realdeo 2014年

8

涂料

Turncoat认为,由于到目前为止还有其他战斗人员,多数人在每一回合之后在善恶之间的交往比停留在同一侧的更多。因此,他在第一轮比赛中一意孤行,表现良好,然后每轮交替,以试图更多地留在输赢的队伍中。

package Humans;

public class Turncoat extends Human {
    public final String takeSides(String history) {
        String[] hist = history.split(",");

        return (hist.length % 2) == 0 ? "good" : "evil";
    }
}

在写完这篇文章之后,我意识到由于基于统计分析的条目,势头将导致多数人在完成更多回合后就不会改变立场。因此,懒惰的外套。

懒惰的外套

懒惰的外套开始像特技外套,但是随着回合的过去,他变得越来越懒惰,并且懒惰者切换到另一侧。

package Humans;

public class LazyTurncoat extends Human {
    public final String takeSides(String history) {
        int round = history.length() == 0 ? 0 : history.split(",").length;
        int momentum = 2 + ((round / 100) * 6);
        int choice = round % momentum;
        int between = momentum / 2;

        return choice < between ? "good" : "evil";
    }
}

2
懒惰的外套很棒!
Angelo Fuchs 2014年

如果您不介意的话,我也将两者包括在内。
Rainbolt 2014年

前进。我很好奇他们两个都将如何做,尤其是与汇编投票统计数据的人相比。
jaybz 2014年

@Rainbolt我刚刚注意到Turncoat的一个愚蠢的错误。无需更正它。它仍然可以正常工作,只是不完全符合预期,即使修复为时不晚,修复它也只会使其表现得完全像较新的条目之一。如果需要,可以随意包含/排除。
jaybz 2014年

8

Ruby传记作家

rounds = ARGV[0].split(',') rescue []

if rounds.length < 10
  choice = 1
else
  outcome_history = ['x',*rounds.map{|r|['0','1'].max_by{|s|r.count s}.tr('01','ab')}]
  player_histories = rounds.map{|r|r.chars.to_a}.transpose.map{ |hist| outcome_history.zip(hist).join }
  predictions = player_histories.map do |history|
    (10).downto(0) do |i|
      i*=2
      lookbehind = history[-i,i]
      @identical_previous_behavior = history.scan(/(?<=#{lookbehind})[10]/)
      break if @identical_previous_behavior.any?
    end
    if @identical_previous_behavior.any?
      (@identical_previous_behavior.count('1')+1).fdiv(@identical_previous_behavior.size+2)
    else
      0.5
    end
  end
  simulations = (1..1000).map do
    votes = predictions.map{ |chance| rand < chance ? 1 : 0 }
    [0,1].max_by { |i| votes.count(i) }
  end
  choice = case simulations.count(1)/10
    when 0..15
      1
    when 16..50
      0
    when 51..84
      1
    when 85..100
      0
  end
end

puts %w[evil good][choice]

我的尝试几乎是聪明的(实际上是聪明的,需要对现场进行测试)。用Ruby编写,所以可能会太慢,但是无论如何在我的机器上,如果有40位随机播放器,这需要0.11秒才能计算出最后一轮,所以我希望它能很好地工作。

另存为biographer.rb,运行为ruby biographer.rb

这个想法是,对于每个玩家,它都会通过查看自己过去10轮的选择以及整体结果,并找出过去相同环境下的情况(他们的票数+总体情况)来估计自己选择“好”的机会。结果)。它选择最长的回溯长度(最多10轮),这样就有任何先例,并使用它来创建频率(根据拉普拉斯的继承法则进行了调整,因此我们永远不会对任何人100%自信)。

然后运行一些模拟,并查看好获胜的频率。如果模拟结果几乎是相同的,则一般而言,它可能会做得很好,因此可以选择预测出的少数派。如果不确定,它将选择预期的多数。


8

犹大

犹大是一个非常好的人。可惜他会出几分钱就背叛好人。

package Humans;

public class Judas extends Human {

    private static final String MONEY = ".*?0100110101101111011011100110010101111001.*?";

    public String takeSides(String history) {
       return history != null && history.replace(",","").matches(MONEY) ? "evil" : "good";
    }
}

1
只有在有足够的参与者的情况下,这才投票给邪恶,您可能想要删除,out history,甚至更多,以便Rusher将游戏分组。
Angelo Fuchs

我不知道他会否将比赛分组。实际上,由于字符串大小,我实际上等待此问题具有足够的提交内容,然后再发布答案。谢谢你让我知道。
威廉·巴博萨

如果您知道如何将60000个字符的参数传递给Windows中的进程,请告诉我。否则,很抱歉弄乱了您的条目,并感谢您修复它!我没想到会收到这么多的意见书。
Rainbolt 2014年

7

荒谬的赌徒(Python)

如果一方连续赢得多数席位,则赌徒意识到下一轮更有可能成为另一方席位(对吗?),这会影响他的投票。他的目标是少数派,因为如果他一次成为少数派,他很可能会多次出现(对吗?)并获得很多积分。

import sys
import random

def whoWon(round):
    return "good" if round.count("1") > round.count("0") else "evil"

if len(sys.argv) == 1:
    print random.choice(["good", "evil"])
else:
    history = sys.argv[1]
    rounds = history.split(",")
    lastWin = whoWon(rounds[-1])
    streakLength = 1
    while streakLength < len(rounds) and whoWon(rounds[-streakLength]) == lastWin:
        streakLength += 1
    lastLoss = ["good", "evil"]
    lastLoss.remove(lastWin)
    lastLoss = lastLoss[0] 
    print lastWin if random.randint(0, streakLength) > 1 else lastLoss  

用法

对于第一轮:

python gambler.py

然后:

python gambler.py 101,100,001 etc.

4
我喜欢您对代码的把握,对吗?:P
IEatBagels 2014年

7

元胞自动机

这使用了Conway的《人生游戏》的常规规则来取胜。首先,根据先前的投票创建2D网格。然后,将“世界”前进一个阶段,并计算剩余的活细胞总数。如果该数目大于单元总数的一半,则选择“好”。否则,选择“邪恶”。

请原谅任何错误,这在我的午餐时间被捣烂了。;)

package Humans;

public class CellularAutomaton extends Human {

    private static final String GOOD_TEXT = "good";

    private static final String EVIL_TEXT = "evil";

    private int numRows;

    private int numColumns;

    private int[][] world;

    @Override
    public String takeSides(String history) {
        String side = GOOD_TEXT;

        if (history.isEmpty()) {
            side = Math.random() <= 0.5 ? GOOD_TEXT : EVIL_TEXT;
        }

        else {
            String[] prevVotes = history.split(",");

            numRows = prevVotes.length;

            numColumns = prevVotes[0].length();

            world = new int[numRows][numColumns];

            for (int i = 0; i < numColumns; i++) {
                for (int j = 0; j < numRows; j++) {
                    world[j][i] =
                        Integer.parseInt(Character.toString(prevVotes[j].charAt(i)));
                }
            }

            int totalAlive = 0;
            int total = numRows * numColumns;
            for (int i = 0; i < numColumns; i++) {
                for (int j = 0; j < numRows; j++) {
                    totalAlive += getAlive(world, i, j);
                }
            }
            if (totalAlive < total / 2) {
                side = EVIL_TEXT;
            }
        }

        return side;
    }

    private int getAlive(int[][] world, int i, int j) {
        int livingNeighbors = 0;

        if (i - 1 >= 0) {
            if (j - 1 >= 0) {
                livingNeighbors += world[j - 1][i - 1];
            }
            livingNeighbors += world[j][i - 1];
            if (j + 1 < numRows) {
                livingNeighbors += world[j + 1][i - 1];
            }
        }
        if (j - 1 >= 0) {
            livingNeighbors += world[j - 1][i];
        }
        if (j + 1 < numRows) {
            livingNeighbors += world[j + 1][i];
        }
        if (i + 1 < numColumns) {
            if (j - 1 >= 0) {
                livingNeighbors += world[j - 1][i + 1];
            }
            livingNeighbors += world[j][i + 1];
            if (j + 1 < numRows) {
                livingNeighbors += world[j + 1][i + 1];
            }
        }

        return livingNeighbors > 1 && livingNeighbors < 4 ? 1 : 0;
    }
}

1
我从代码中删除了打印行以进行测试。Java条目仅需要返回是好是坏,而无需打印。
Rainbolt 2014年

7

里奇教授

我希望允许使用库,不要在没有一个=的情况下这样做

基本思想是使用每轮之前的30个结果作为特征,在最后一轮中为每个参与者训练岭回归分类器。最初包括所有玩家的最后一轮结果,以预测每个玩家的结果,但是随着参与者人数的增加(例如50个左右),时间缩短了。

#include <iostream>
#include <string>
#include <algorithm>
#include "Eigen/Dense"

using Eigen::MatrixXf;
using Eigen::VectorXf;
using Eigen::IOFormat;
using std::max;

void regress(MatrixXf &feats, VectorXf &classes, VectorXf &out, float alpha = 1) {
    MatrixXf featstrans = feats.transpose();
    MatrixXf AtA = featstrans * feats;

    out = (AtA + (MatrixXf::Identity(feats.cols(), feats.cols()) * alpha)).inverse() * featstrans * classes;
}

float classify(VectorXf &weights, VectorXf &feats) {
    return weights.transpose() * feats;
}

size_t predict(MatrixXf &train_data, VectorXf &labels, VectorXf &testitem) {
    VectorXf weights;
    regress(train_data, labels, weights);
    return (classify(weights, testitem) > 0 ? 1 : 0);
}

static const int N = 30;
static const int M = 10;
// use up to N previous rounds worth of data to predict next round
// train on all previous rounds available
size_t predict(MatrixXf &data, size_t prev_iters, size_t n_participants) {
    MatrixXf newdata(data.rows(), data.cols() + max(N, M));
    newdata << MatrixXf::Zero(data.rows(), max(N, M)), data;

    size_t n_samples = std::min(500ul, prev_iters);
    if (n_samples > (8 * max(N, M))) {
        n_samples -= max(N,M);
    }
    size_t oldest_sample = prev_iters - n_samples;
    MatrixXf train_data(n_samples, N + M + 1);
    VectorXf testitem(N + M + 1);
    VectorXf labels(n_samples);
    VectorXf averages = newdata.colwise().mean();
    size_t n_expected_good = 0;
    for (size_t i = 0; i < n_participants; ++i) {
        for (size_t iter = oldest_sample; iter < prev_iters; ++iter) {
            train_data.row(iter - oldest_sample) << newdata.row(i).segment<N>(iter + max(N, M) - N)
                                  , averages.segment<M>(iter + max(N, M) - M).transpose()
                                  , 1; 
        }
        testitem.transpose() << newdata.row(i).segment<N>(prev_iters + max(N, M) - N)
                  , averages.segment<M>(prev_iters + max(N, M) - M).transpose()
                  , 1;
        labels = data.row(i).segment(oldest_sample, n_samples);
        n_expected_good += predict(train_data, labels, testitem);
    }
    return n_expected_good;
}


void fill(MatrixXf &data, std::string &params) {
    size_t pos = 0, end = params.size();
    size_t i = 0, j = 0;
    while (pos < end) {
        switch (params[pos]) {
            case ',':
                i = 0;
                ++j;
                break;
            case '1':
                data(i,j) = 1;
                ++i;
                break;
            case '0':
                data(i,j) = -1;
                ++i;
                break;
            default:
                std::cerr << "Error in input string, unexpected " << params[pos] << " found." << std::endl;
                std::exit(1);
                break;
        }
        ++pos;
    }
}

int main(int argc, char **argv) {
    using namespace std;

    if (argc == 1) {
        cout << "evil" << endl;
        std::exit(0);
    }

    string params(argv[1]);
    size_t n_prev_iters = count(params.begin(), params.end(), ',') + 1;
    size_t n_participants = find(params.begin(), params.end(), ',') - params.begin();

    MatrixXf data(n_participants, n_prev_iters);
    fill(data, params);

    size_t n_expected_good = predict(data, n_prev_iters, n_participants);

    if (n_expected_good > n_participants/2) {
        cout << "evil" << endl;
    } else {
        cout << "good" << endl;
    }
}

编译

将源代码保存在一个名为的文件中ridge_professor.cc,下载Eigen库并将内部找到的Eigen文件夹解压缩到与源文件相同的文件夹中。用编译g++ -I. -O3 -ffast-math -o ridge_professor ridge_professor.cc

跑步

调用ridge_professor.exe并根据需要提供参数。

由于我还无法在任何地方发表评论,因此我将在这里问:Windows上的参数大小限制是否使得无法在几百个回合中调用具有整个历史记录的二进制文件?我以为参数中不能超过9000个字符...


感谢您对我的绘画注意这个。如果在Java中还不能正常工作,我将想出一些方法使其工作。如果Java无法做到这一点,研究会告诉我C ++可以,并且我将借此机会重新学习C ++。我很快会回来的测试结果。
Rainbolt 2014年

事实证明,Java不受命令提示符的限制。似乎只有大于32k的命令才会引起问题。这是我的证明(我自己写的):docs.google.com/document/d/…。再次,我非常感谢您在明天开始试用之前提出此问题。
Rainbolt 2014年

@Rusher There are already 57 bots and you plan on each run being composed of 1000 rounds. That would make your string 57k characters (therefore >32k), wouldn't it?
plannapus

1
@Rusher I think it may be better to extend the timeline by another week and ask participants to change their programs to read stdin instead of using an argument string. Would be trivial for most programs to change
dgel

@dgel The timeline for the challenge is infinitely long, but I don't want to change the rules in a way that everyone has to rewrite their answer. I'm pretty sure that the rule I added last night will only screw over a single submission, and I plan on helping that person if he ever gets his program to a point where it compiles.
Rainbolt

6

Crowley

Because the Winchesters are much less interesting without this fellow. He obviously sides with evil...unless it is needed to take care of a bigger evil.

package Humans;

public class Crowley extends Human {
public String takeSides(String history) {
    int gd = 0, j=history.length(), comma=0, c=0, z=0;
    while(comma < 2 && j>0)   {
        j--;
        z++;
        if (history.charAt(j) == ',') {
            comma++;
            if(c> z/2) {gd++;}
            z=0;
            c=0;
        } else if (history.charAt(j)=='1') {
            c++;
        } else {
        }
    }
    if(gd == 0){
        return "good";
    } else {
        return "evil";
    }
}}

I look at the last two turns (0 commas so far and 1 comma so far) and if both of them let evil win, I vote good. Otherwise I vote evil.


Do I get this right? You look at the last turn and if less than 50% are "good" votes you side with "good" else with evil? (Out of curiosity: Do you prefer cryptic variable names or is it an accident?)
Angelo Fuchs

1
@AngeloNeuschitzer I look at the last two turns (0 commas so far and 1 comma so far) and if both of them let evil win, I vote good. Otherwise I vote evil. I prefer variable names that are short to type if the code is short enough the purpose of the code will not get confused. I'm not a professional programmer and this was the first time I've programmed in java or something someone else saw the code for in 6.5 years. I wrote this to refresh my memory.(TLDR they aren't cryptic to me and I'm the only one I usually code for.)
kaine

For clarity... Crowley started out as a human so it was intentional he starts good...Did not expect him to stay good for all rounds though... damn
kaine
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.