Good Versus Evil

112

47

Results - July 19, 2014

The current King of the Hill is Mercenary by user Fabigler! Keep submitting entries and knock him off of his throne!

Click here to view the Scoreboard.

Programs submitted on or before July 19, 2014 were included. All other submissions will be included in future trials. New results should be posted around August 9, so that gives you plenty of time.


Illustration drawn by brother Illustrated by Chris Rainbolt, my brother and a fresh graduate from Savannah College of Art and Design

Introduction

The angels and demons are fighting and, as usual, using earth as their battleground. Humans are stuck in the middle and are being forced to take sides. An unknown neutral force rewards those who consistently fight for the losing side.

The Game

Each trial, you will be pseudorandomly paired and then shuffled with between 20 and 30 other submissions. Each trial will consist of 1000 rounds. Each round, you will be passed an input and be expected to produce output. Your output will be recorded and scored. This process will be repeated 1000 times.

Input

You will receive a single argument that represents the past votes of each player. Rounds are delimited by comma. A 0 represents a player who sided with Evil that round. A 1 represents a player who sided with Good. Within a trial, the players will always be in the same order. Your own vote will be included, but not explicitly identified. For example:

101,100,100

In this example, three rounds have been completed and three players are competing. Player one always sided with Good. Player two always sided with Evil. Player three swapped from Good in round 1 to Evil in rounds 2 and 3. One of those players was you.

Output

Java Submissions

  • Return the string good if you want to side with Good.
  • Return the string evil if you want to side with Evil.

Non-Java Submissions

  • Output the string good to stdout if you want to side with Good.
  • Output the string evil to stdout if you want to side with Evil.

If your program outputs or returns anything else, throws an exception, does not compile, or takes longer than one second to output anything on this exact machine, then it will be disqualified.

Scoring

Scores will be posted in a Google docs spreadsheet for easy viewing as soon as I can compile all the current entries. Don't worry - I will keep running trials for as long as you guys keep submitting programs!

  • You receive 3 points for siding with the majority during a round.
  • You receive n - 1 points for siding with the minority during a round, where n is the number of consecutive times you have sided with the minority.

Your score will be the median of 5 trials. Each trial consists of 1000 rounds.

Deliverables

Non-Java Submissions

You must submit a unique title, a program, and a Windows command line string that will run your program. Remember that an argument may be appended to that string. For example:

  • python Angel.py
    • Note that this one has no args. This is round one! Be prepared for this.
  • python Angel.py 11011,00101,11101,11111,00001,11001,11001

Java Submissions

You must submit a unique title and a Java class that extends the abstract Human class written below.

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

Testing

If you want to test your own submission, follow the instructions here.

Additional Notes

You may submit as many different submissions as you want. Submissions that appear to be colluding will be disqualified. The author of this challenge will be the only judge on that matter.

A new instance of your program or Java class will be created every time it is called upon. You may persist information by writing to a file. You may not modify the structure or behavior of anything except your own class.

Players will be shuffled before the trial starts. Demon and Angel will participate in every trial. If the number of players is even, Petyr Baelish will also join. Demon fights for Evil, Angel for Good, and Petyr Baelish chooses a pseudorandom side.

Rainbolt

Posted 2014-07-08T20:18:06.290

Reputation: 6 176

Is there a submission deadline? – Jonathan Pullano – 2014-07-09T15:18:59.853

2Comments purged, as they were obsolete and at request of OP. Please notify me of any comments that need to be undeleted. – Doorknob – 2014-07-16T11:28:29.823

@Doorknob The "When's the official testing?" question should stick around / be incorporated into the main post. – isaacg – 2014-07-17T09:57:11.803

@isaacg I've undeleted those two comments until Rusher can edit them into his post. – Doorknob – 2014-07-17T10:40:10.970

7Woah, OP changes his username. Ok, so when will the result be displayed? – justhalf – 2014-07-18T06:52:58.687

2Bounty refunded and reinstated as the OP couldn't compile all the submissions in time. Minimum time for the new bounty to be awarded is 24 hours, so that's approximately when the new one will be awarded. – Doorknob – 2014-07-19T07:19:06.893

6@Rainbolt This must be one freakin' hell of a job, running this challenge! The reason for this amount of attention is the simplicity of the protocol and the rules, making it accessible while also allowing simple, working entries. TL;DR: Your challenge is too good! :D – tomsmeding – 2014-07-19T18:54:20.987

@Rainbolt I would suggest applying a cut date for the current round of testing to limit it to the ones that are already there so you don't have to do it all over again if something new pops up. – Angelo Fuchs – 2014-07-20T08:15:56.290

@Rainbolt: could you post upper and lower bounds on scores as well as averages? – dgel – 2014-07-21T10:41:51.810

3@dgel I'll post the raw data, upper, lower, averages, and maybe a line chart so we can see who did better as the competition dragged on. – Rainbolt – 2014-07-21T12:03:06.023

6One of the pods ended up with 10 entries that voted the same way every single time. Consequently, two users ended up with perfect or "one round short of perfect" scores of around 450,000. The same entries scored around 1900 in other trials. The average score is close to 2000. Because of the extreme imbalance in results, I decided that a more meaningful number would be a median. I edited the challenge so that after 5 trials, the winner will be the submission with the highest median. If anyone thinks that moving from mean to median is unfair or otherwise a poor choice, please comment. – Rainbolt – 2014-07-21T18:57:16.903

@Rainbolt Another option would be to issue points based where each entry placed in each round (i.e. how many other entries that entry outscored), instead of making absolute scores important. I favor that idea, although I don't suppose it matters very much to me, really. – Brilliand – 2014-07-21T19:34:13.490

1Either median or summing the ranking in each of the 3 trials is fine by my – dgel – 2014-07-21T20:08:22.333

Probably a bit late, but wouldn't it be better to do around 100 rounds for around 100 trials instead of 1000/3? Besides that, I also think the average rank would be a better measure of performance than the average score. – moooeeeep – 2014-07-22T07:06:53.893

The problem with doing just 100 rounds is that some of the entries were designed around the fact that there will be 1000 rounds. In fact, a number of the statistics-based entries vote randomly for the first n rounds (and I think 100 was the popular choice) for enough data to be available for statistical analysis. – jaybz – 2014-07-22T08:28:24.180

1I was going to add this as an edit, but it doesn't fit. Anyway turns out I might have been wrong. I did a quick check and I found only two entries that was specifically designed for more than 100 rounds. That said, 100 is a much smaller sampler size than 1000 and would negatively affect the statistics-based entries severely. – jaybz – 2014-07-22T08:43:00.033

1Thanks for posting the scoreboard! It looks like the first few rounds may be getting cut off (or skipped, or something)--Biographer is supposed to choose Good for the first ten rounds, but turns to evil around round 6, and Linus isn't matching his hardcoded behavior either. – histocrat – 2014-07-22T16:37:45.023

@histocrat I will investigate this later. If I find that the posted results don't match the actual results, I'll take the blame and award another bounty (because what am I doing with all of these imaginary points anyway?). This could just be an error on my part with matching the raw data to the scores. – Rainbolt – 2014-07-22T17:17:12.493

How come my Scientists aren't in there? – tomsmeding – 2014-07-22T18:07:19.603

@tomsmeding I expect that you aren't the only one with this question. Please see the note on the side of the Scoreboard. I can't say for certain why you were excluded. When I have time to review my detailed notes at home, I'll transcribe them to the Scoreboard, and then you'll have your answer. I will accept appeals if you feel like I messed up, because I mess up a lot. Hopefully everyone here understands how large of a project this was, that it's all just a game anyway, and that I'll keep fixing it until I get it right. – Rainbolt – 2014-07-22T18:22:34.460

Ok so what ended up happening was when you separated us into groups, the scores were highly dependant on who we are paired with. As I was fortunate enough to be sided with goody two shoes and opportunists (literally), every round from mine went to good so I lost every round (if I won I would consider switching). I, therefore, got the one perfect score. You noted that this is not really fair (good observation) as mine could not hold weight on a more variable field. You chose to score by median, therefore. I'm not sure that is the best measure, but mine did not diserve to win so thats fine. – kaine – 2014-07-22T19:14:31.193

I thought Angel and Devil would be in each group? Obviously I misunderstood this. – Paŭlo Ebermann – 2014-07-22T19:57:57.083

@anotherguest I chose median because it is the most resistant to being swayed by outliers. A simple average varied from 1800 to 100000. A geometric mean using only the three middle scores ranged from 1700 to 5000. The median ranged from 1700 to 2900. The median has the tightest grouping, which indicates to me that it will change the least as I run more and more trials. Wikipedia will back me up here: [The median] is the most resistant statistic, having a breakdown point of 50%: so long as no more than half the data is contaminated, the median will not give an arbitrarily large result. – Rainbolt – 2014-07-22T20:05:09.080

@PaŭloEbermann Angel and Demon participated in every trial. They were shuffled and separated into pods just like the rest of the submissions. – Rainbolt – 2014-07-22T20:07:48.940

@Rainbolt Rainbot, just to be clear I'm in agreement for the most part for eliminating outliers. I do believe median works alot better when there are alot of samples involved as it helps to distinguish what actually is an outlier. For instance, Whatever did great on two separate ones...are either of them outliers? Nevertheless, you are the boss and the call was good. – kaine – 2014-07-22T21:03:28.000

Just to be clear, the issue with median can be seen from this result. Mercenary did quite well in 3 rounds, rounds 1,2 and 3, and quite poorly in rounds 4 and 5, leading to it having a very high median. I think a statistic that incorporates all of the results, but doesn't scale with any of them, might be better. – isaacg – 2014-07-22T22:57:37.587

@histocrat It turns out that Biographer was calling another entry (python BigMac.py... don't ask why). If Biographer is in the top 3 the next time I post the scoreboard, I'll make it up to you. Linus, as far as I can tell, is behaving just as expected. To everyone else, the scores should be correct. I just messed up in this one particular case. – Rainbolt – 2014-07-23T02:03:18.587

1@tomsmeding and all other participants: I added a disqualifications tab to the scoreboard and summarized my notes. It's likely that I made mistakes at some point. For that reason, I added an extremely simple to follow appeal process. Just visit the Scoreboard and read the rules, then leave a comment here if you have something to say. – Rainbolt – 2014-07-23T03:49:36.660

@Rainbolt I fully believe my stuff timed out, but I didn't expect an exception. Do you have any more information on Scientist's exception? – tomsmeding – 2014-07-23T07:53:14.657

@Rainbolt, my entries (RidgeProfessor and NaiveBayesian) aren't in the scoreboard, and not listed amongst the disqualifications either. Could you tell me what went wrong? – dgel – 2014-07-23T08:11:53.853

@dgel I added a note to one of your Eigen entries. In short, I forgot to add them as "Couldn't compile". – Rainbolt – 2014-07-23T13:07:41.760

1@Rainbolt, do we get a next round? – Emil – 2014-08-11T15:28:05.897

2@Emil Yes. It has been nearly a month so I had better get to it. I have been wanting to run some other challenges, but promised myself I would wait until after I got a new scoreboard posted for this one. I "undisqualified" everyone to give them a second chance. Unfortunately, this means that some people will start failing 7 hours into the rounds, making me start all the way over, so it will be a few days before I can post the new scoreboard. – Rainbolt – 2014-08-12T16:42:20.813

Great. Thanks for the hard work. I'm looking fortward to seeing the results. – Emil – 2014-08-13T06:28:23.200

Is the new scoreboard going to be up any time soon? – overactor – 2014-08-28T11:16:35.693

2@overactor I gave up on running trials. Every output file looks like a bunch of Chinese characters and other nonprintable characters. Previously, this only occurred once every two or three trials. Now, it's messing up every output file. It takes about 7 hours to run a trial, so it is really tedious to watch over (I have to keep checking my graphs to make sure that nothing interferes with my CPU or memory usage). If you want, I can post the source code this afternoon. It has every single submission included. Maybe someone else can get it to work. – Rainbolt – 2014-08-28T13:30:52.353

2I would appreciate it is you would post the source. I likely won't be able to help get a new scoreboard up, but I want to learn how you set it all up and really hope someone else does. This was an interesting puzzle and the results were underwelming. – kaine – 2014-09-03T20:03:10.487

Answers

11

The Mercenary

Always sides with the one who paid the most money last round.

Taking into account that good people earn statistically more.

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

fabigler

Posted 2014-07-08T20:18:06.290

Reputation: 626

2

This is the second post to say something about money. Am I missing a reference or something?

– Rainbolt – 2014-07-14T21:31:22.973

True, but this guy is an even more evil bastard. Deserting his pals every turn, only for the sake of money. – fabigler – 2014-07-14T21:34:07.630

Your switch statement was missing a return statement for the default case, causing it to not compile. I added a random one. – Rainbolt – 2014-07-15T00:47:32.560

@Rusher Thank you very much! – fabigler – 2014-07-15T05:32:00.983

Hmm, but how do you know that your own entry is the last one? You just do the opposite of what the guy which was sorted last did last round, which could actually be the same thing each round. – Paŭlo Ebermann – 2014-07-15T19:05:50.490

@Rusher Changed the class a bit, now. Could you submit this, and check if it works? – fabigler – 2014-07-15T20:18:20.143

@PaŭloEbermann You're right. Should have readen it more carefully. Edited the class a bit, now – fabigler – 2014-07-15T20:18:39.973

@fabigler Sure. Here are the results with just you and a couple of other players: {{Angel,499500},{Mercenary,3000},{Demon,3000}}. This is probably not a good indication of how you would do with more players involved, but it compiles and runs for 1000 rounds with no problem. – Rainbolt – 2014-07-15T20:31:55.607

@Rusher OK thank you very much! – fabigler – 2014-07-15T20:35:44.753

The longest your submission took to respond with 21 players over 1000 rounds was 0.045 seconds in round 4. On average, you flopped between 0.02 and 0.30 repeatedly. The other players were all Angels or Demons, so the results may still not be too accurate. I'm just bored and playing with the timers lol. – Rainbolt – 2014-07-15T20:53:32.113

@Rusher nice, thanks for your effort! – fabigler – 2014-07-16T21:22:22.000

4Congratulations, King of the Hill! I don't understand how this entry wins. Care to add an explanation, now that it has a 300 reputation bounty attached to it? – Rainbolt – 2014-07-22T16:10:23.363

@Rainbolt: Do have an example transcript (i.e. against which other "Humans" it did run, and how they did vote (the input to the last round))? – Paŭlo Ebermann – 2014-07-22T19:43:10.527

@PaŭloEbermann You can see which submissions Mercenary was paired against by looking at the Scoreboard, also posted at the top of the challenge. Unfortunately, someone has pointed out that there may be a problem with the scores. Once I verify, I may award another bounty.

– Rainbolt – 2014-07-22T19:48:00.953

4Possibly a bug, or I misunderstood the comments and description, but the Mercenary doesn't actually do what it was meant to do. Except for the first random round, he will always side with evil unless less than 1/6 of the people voted for evil on the previous round. – jaybz – 2014-07-23T11:17:23.823

It's been really fun reading through the submissions. I'm also very surprised that this one (which is actually quite simple, but is slightly obfuscated in its implementation) came out on top of all the other far more intelligent bots. The dynamics of this competition are apparently very chaotic – Steven Lu – 2014-08-20T21:08:46.733

39

Hipster, Ruby

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

Simply goes with last round's minority, just because everything else is mainstream.

Run like

ruby hipster.rb

Martin Ender

Posted 2014-07-08T20:18:06.290

Reputation: 184 808

30

Petyr Baelish

You never know whose side Petyr Baelish is on.

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

This entry will only be included if the number of players is even. This ensures that there will always be a majority.

Rainbolt

Posted 2014-07-08T20:18:06.290

Reputation: 6 176

28On Petyr Baelish's side, obviously. – Cthulhu – 2014-07-09T07:25:31.380

I doubt this will do well. You either want to be on the winning side or the losing side consistently, and Peter will change sides too quickly to win regularly or take advantage of the bonus for consistently choosing the losing side. – Kevin – 2014-07-09T20:15:45.703

2@Kevin It consistently beats most of the bots. It usually scores 27ish. – cjfaure – 2014-07-09T20:17:04.497

3@Kevin This entry was submitted by the author of the challenge. It wasn't meant to do well. It exists to make sure that there will always be a majority, because with an even number of players, there could be a tie. – Rainbolt – 2014-07-09T20:17:10.350

I know it's one of the canonical entries. I just don't think it's going to do well =) – Kevin – 2014-07-09T20:20:33.103

4Why oh God why has this one got the most votes? It's just not fair. – tomsmeding – 2014-07-09T20:27:03.800

@Rusher That edit referred to the Scientist, didn't it? You sneaky bastard! – tomsmeding – 2014-07-09T20:53:49.287

3@tomsmeding No. It's a quote from Game of Thrones lol. – Rainbolt – 2014-07-09T20:55:34.650

2Just a comment in passing, but it's conventional to compare to Math.random() using <, because Math.random() < x is true with a probability of x. Clearly it doesn't matter that much for 0.5, but convention is worth something :) – hobbs – 2014-07-15T02:26:48.023

@hobbs I hope you continue leaving little nuggets of wisdom around here, because I liked that one. Fixed. – Rainbolt – 2014-07-15T02:40:26.893

29

C++, The Meta Scientist

This one does essentially the same as The Scientist, but doesn't operate on rounds as a whole but on the individual players. It tries to map a wave (or a constant function) to each player separately and predicts their move in the next round. From the resulted round prediction, The Meta Scientist chooses whichever side looks like having a majority.

#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 you want to turn on debug statements, change the line reading #if 0 to #if 1.

Compile with g++ -O3 -std=c++0x -o MetaScientist MetaScientist.cpp (you don't need warnings, so no -Wall) and run with MetaScientist.exe (possibly including the argument of course). If you ask really nicely I can provide you with a Windows executable.

EDIT: Apparently, the previous version ran out of time around 600 rounds into the game. This shouldn't do that. Its time consumption is controlled by the #define WINDOW (...) line, more is slower but looks further back.

tomsmeding

Posted 2014-07-08T20:18:06.290

Reputation: 2 034

2I humbly suggest you try picking the losing side. If you can consistently guess correctly, you'll get more than 3 points per round. – Kevin – 2014-07-09T20:18:38.870

1@Kevin That's true, but I figured that it might guess the wrong side pretty quickly, and you need to correctly guess the losing side more than seven times in a row to get an improvement over always getting the majority right. I might change it though. – tomsmeding – 2014-07-09T20:23:00.270

1@Kevin Also, I'd first like to see how these do (Scientist and Meta Scientist) when Rusher gets us a scoreboard this weekend, as he indicated in the comments to the OP. Rusher, sorry, but I'm too lazy to compile all the stuff myself... :) – tomsmeding – 2014-07-09T20:28:17.897

3No worries! It probably isn't safe to run these anyway. Just let me screw up my machine with code written by 50 strangers on the Internet. – Rainbolt – 2014-07-09T20:39:46.257

@Rusher Sorry! Quite funny, actually. I promise you my code does nothing inappropriate and just conforms to the challenge... (although indeed I am an internet stranger... How can I convince someone of something?) Oh and, advice: run it in a virtual machine (I suggest VirtualBox with some kind of linux distro or something) if you don't trust the submissions. I also have a Windows 7 iso for you. – tomsmeding – 2014-07-09T20:41:17.280

Perhaps you could spin up two more scientists that vote for the losing side? Then you've got four entries... – Kevin – 2014-07-09T20:42:46.107

I'm not worried at all. I have a testing box already set up from the last challenge I ran. If it dies, I have Darrek's (Darins? I can't remember the name) Boot and Nuke handy on a CD, and a legal copy of Windows on a thumb drive. – Rainbolt – 2014-07-09T20:43:13.123

1@Kevin But that's so MANY! I can, indeed, but I don't like it. I'll see how these fare. – tomsmeding – 2014-07-09T20:43:42.993

26

Angel

The purest player of all.

Program

print "good"

Command

python Angel.py

Rainbolt

Posted 2014-07-08T20:18:06.290

Reputation: 6 176

22Python is a good language. It seems only natural that the Angel should use it. – jpmc26 – 2014-07-10T01:24:24.980

23May I remind people that a Python is a Snake. A Serpent. – Mr Lister – 2014-07-15T07:52:05.107

3@MrLister May I remind you that Lucifer was a great Angel before God cast him out of heaven? – Zibbobz – 2014-07-18T18:36:34.510

1@Zibbobz Yeah... shame really, that they fell out. They could have achieved so much together. – Mr Lister – 2014-07-18T20:03:49.437

24

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

In Book 7, The Atlantis Complex, Artemis Fowl contracted a psychological disease (called Atlantis complex) that forced him to do everything in multiples of 5 (speaking, actions, etc). When he couldn't do it in some multiple of 5, he panicked. I do basically that: see if good or evil (intentional bias) is divisible by 5, if neither is, then I panic & see which was greater & run with that or panic even further & randomly choose.

Kyle Kanos

Posted 2014-07-08T20:18:06.290

Reputation: 4 270

4When I read Artemis Fowl in Junior High, only two books existed. It's nice to see that there are now seven, and that Disney is making it into a movie. – Rainbolt – 2014-07-09T01:11:11.823

1There's actually 8 books. – Kyle Kanos – 2014-07-09T01:12:19.937

7The more the merrier (unless you are reading The Wheel of Time) – Rainbolt – 2014-07-09T01:13:36.413

Never read The Wheel of Time, so whatever reference there is with that comment, it's lost on me. – Kyle Kanos – 2014-07-09T01:14:11.163

Shouldn't there be semicolons after the return statements? – CommonGuy – 2014-07-09T07:45:08.887

1And you forgot break; in your switch. – johnchen902 – 2014-07-09T12:33:16.690

1@johnchen902,@Manu: I am not very experienced in java (I use Fortran90+ & only see java here), hence my errors. I'll fix them when I get into the office in an hour. – Kyle Kanos – 2014-07-09T12:37:48.343

1Okay, maybe it was 20 minutes, not an hour. Misjudged my morning workload. – Kyle Kanos – 2014-07-09T13:01:06.953

I'd really love to see you write a bot for this in FORTRAN. Languages I don't see often always get an "interesting" bonus from me. – trlkly – 2014-07-13T15:49:34.533

Why not good % 5 == 4 or similar, so that Artemis fixes things to be multiples of 5? – James Wood – 2014-07-14T15:46:44.837

19

Disparnumerophobic

Odd numbers are terrifying.

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

pseudonym117

Posted 2014-07-08T20:18:06.290

Reputation: 1 053

17Comment made me laugh/snort. – phyrfox – 2014-07-09T03:50:21.303

17

Linus, Ruby

Seeks to confound analysts by always breaking the pattern.

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

Save as linus.rb and run with ruby linus.rb

histocrat

Posted 2014-07-08T20:18:06.290

Reputation: 20 600

16

The BackPacker

Determinates a player that has chosen the matching minority the most yet and chooses his last vote.

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

The CrowdFollower

Determinates a player that has chosen the matching majority the most yet and chooses his last vote.

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

Angelo Fuchs

Posted 2014-07-08T20:18:06.290

Reputation: 295

Very clean program! – Rainbolt – 2014-07-10T13:21:35.760

Whoops, I think I may have copied your program in a different language. – PyRulez – 2014-07-14T00:55:04.810

@Rusher I updated the code and would like to add this as two entries, one with goWithMajority = true and one where its false. Is that okay, or do I need to add a second BackPacker for this? – Angelo Fuchs – 2014-07-17T09:30:52.577

@AngeloNeuschitzer I edited this post. This way, I won't forget to add both submissions. I suggest you change the really uncreative name I gave it, and maybe add a description to both if you want. – Rainbolt – 2014-07-17T13:03:25.537

@Rusher Thanks. I renamed it and added a description. – Angelo Fuchs – 2014-07-17T13:06:15.213

You reference a variable called "good" without ever declaring it. I assumed you mean "playerVotedGood" so that's what I used. It was the only other boolean in close proximity. Ping me if I was incorrect. – Rainbolt – 2014-07-19T06:35:58.840

@Rainbolt You are correct. I fixed it in the post as well. Thanks for the info. – Angelo Fuchs – 2014-07-19T09:24:52.833

1@Rainbolt I like your FrontPacker better, actually. Lol'd. – tomsmeding – 2014-07-19T18:57:00.970

15

C++, The Scientist

This one tries to, with the history of what the majority chose per round in wave (majority() gives the majority's choice on a round), fit a wave to the data, of wavelength 2*period and phase phase. Thus, given 0,1,1,1,0,1,0,1,1,1,0,0,0,1,0 it selects period=3, phase=5 (maxat=={3,5}): its scores become 9 3 11 5 5 3 5 7 9 7 7 7 7 7 7. It loops over all possible periods and if, for that period, the score is higher than for the current maximum, it stores {period,phase} for which that occurred.

It then extrapolates the found wave to the next round and takes the predicted majority.

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

Compile with g++ -O3 -std=c++0x -o Scientist Scientist.cpp (you don't need warnings, so no -Wall) and run with Scientist.exe (possibly including the argument of course). If you ask really nicely I can provide you with a Windows executable.

Oh, and don't dare messing with the input format. It'll do strange things otherwise.

EDIT: Apparently, the previous version ran out of time around 600 rounds into the game. This shouldn't do that. Its time consumption is controlled by the #define WINDOW (...) line, more is slower but looks further back.

tomsmeding

Posted 2014-07-08T20:18:06.290

Reputation: 2 034

8Downloading executables written by sixty+ strangers on the Internet seems like a bad idea. – Rainbolt – 2014-07-14T22:02:07.303

@Rusher I totally agree. If you do want problems, that's step one in the "for dummies" guide. My offer stands though :) – tomsmeding – 2014-07-15T06:14:45.907

2Got this one to compile (and compete) fine. – Rainbolt – 2014-07-19T07:47:42.913

15

Fortune Teller

This is still work in progress. I haven't tested it yet. I just wanted to see if the OP thinks it breaks the rules or not.

The idea is to simulate the next round by executing all other participants a few times to get a probability of the outcome and act accordingly.

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

}

Andris

Posted 2014-07-08T20:18:06.290

Reputation: 406

If your bot runs all the other bots each turns before answering, won't it take more than 1s to answer? – plannapus – 2014-07-09T12:41:49.057

@plannapus I'm going to guess the assumption with this bot is that everyone else is going to err on the side of caution and avoid anything close 1 seconds worth of wait. I'm thinking it may be worthwhile submitting and entry that consists of a 0.9 second wait, before returning "good", just to mess with him. Actually, SBoss has beat me to it :D – scragar – 2014-07-09T12:44:25.537

Yahhh! Then I would have to blacklist that bot in my code. That would be frustrating... Also with different entries in different environments like Python or Perl the reprated loading of the interpreter might just be enough to bring this code above the time limit. – Andris – 2014-07-09T12:55:29.540

Substitute my Homer with Angel :) – SBoss – 2014-07-09T13:00:53.590

I was thinking more along the lines of executing the other bots in paralell or to kill them if they take more than a few milliseconds. – Andris – 2014-07-09T13:04:00.313

@Rusher Would you consider the execution of other bots via reflection acceptable? I guess if other solutions save their state and depend on the assumption of being called only once each round than this could mess with their functionality which is against the rules, although none of them does this so far. – Andris – 2014-07-09T16:45:18.653

16If someone else does the same thing as this, you get an infinite loop. – Brilliand – 2014-07-09T17:10:02.987

@Brilliand Good point! We could also get a stackoverflow. This could be mitigated by a timeout, catching the exception or using a static field to detect recursive execution of my code. – Andris – 2014-07-09T17:13:48.573

@Andris Also, check the stack trace to see if your bot is being called by another bot. If it is, fight back. – Brilliand – 2014-07-09T17:22:47.430

Your code uses a variable called classes that is not declared, and so it doesn't compile. You can instantiate other bots, but you cannot modify their behavior or structure. I'm confident that this won't ruin the competition, because I don't think you will get an accurate prediction given the time constraints you are bound to. Please prove me wrong! – Rainbolt – 2014-07-09T18:36:46.817

@Rusher Fixed classes issue. My profiling shows that Angel takes about 400ms to execute due to starting Phyton. I guess you were right that it is not really feasible to do this analysis under 1000ms. – Andris – 2014-07-09T21:37:23.060

I haven't tested this, but there should probably be a return inside the if(recursiveCall) ;) – mackthehobbit – 2014-07-12T01:59:59.633

@mackthehobbit Fixed. Thanks! – Andris – 2014-07-13T09:01:29.803

4The submission timed out. I attached a profiler, and nearly half a second is spent calling some submissions. It at least works though, so congrats for that. – Rainbolt – 2014-07-14T00:57:55.317

14

Code Runner

So, to make things interesting, I created a script to automatically download the code from every posted answer, compile it if necessary, and then run all of the solutions according to the rules. This way, people can check how they are doing. Just save this script to run_all.py (requires BeautifulSoup) and then:

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

A few things:

  1. If you want to add support for more languages, or alternatively remove support for some, see def submission_type(lang).
  2. Extending the script should be fairly easy, even for languages that require compilation (see CPPSubmission). The language type is grabbed from the meta code tag < !-- language: lang-java -- >, so make sure to add it if you want your code to be run (Remove the extra spaces before and after the <>). UPDATE: There is now some extremely basic inference to try and detect the language if it is not defined.
  3. If your code fails to run at all, or fails to finish within the allotted time, it will be added to blacklist.text and will be removed from future trials automatically. If you fix your code, just remove your entry from the blacklist and re-run get,

Currently supported languages:

 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
}

Without further ado:

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

WhatAWorld

Posted 2014-07-08T20:18:06.290

Reputation: 721

Why no Fortran language??

– Kyle Kanos – 2014-07-16T18:01:26.293

@KyleKanos - I added support for it, will update the code shortly. – WhatAWorld – 2014-07-16T18:17:56.040

Yay! I (sorta) worked hard on my Fortran submission & Rusher can't get it to work so I'd like someone to get it :) – Kyle Kanos – 2014-07-16T18:19:49.237

Alright, the code has been updated and simplified. See the new instructions for scraping submissions and then running them. – WhatAWorld – 2014-07-16T18:30:59.433

@KyleKanos Haha and you were the one rejecting all the syntax highlighting edits. – Rainbolt – 2014-07-16T18:32:52.880

@Rusher: What's wrong with rejecting that virtually useless feature? – Kyle Kanos – 2014-07-16T18:34:23.793

@KyleKanos I won't get into that here. I'll just point you to the meta topic's top voted answer: http://meta.codegolf.stackexchange.com/a/1120/18487

– Rainbolt – 2014-07-16T18:39:12.437

1@Rusher: I agree with PeterTaylor on this one: syntax highlighting as the only suggested edit should be rejected. Edits should be used for substantial corrections, not minor stuff. – Kyle Kanos – 2014-07-16T18:47:56.667

Perhaps there is a better place for this discussion. Anyway, there is now some very rough inference to try and detect the code type, so the lang tag is no longer absolutely required. If anyone tries this code, please me know how it works. – WhatAWorld – 2014-07-16T18:52:03.850

1You do deserve the rep for this, but since this isn't exactly an answer to the question (and could probably benefit from the community adding stuff for other languages) I think this should technically be a community wiki. – Martin Ender – 2014-07-16T20:26:05.870

13

The Beautiful Mind, Ruby

Makes its decision based on patterns of questionable significance in the bit representation of the last round

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

Run like

ruby beautiful-mind.rb

Martin Ender

Posted 2014-07-08T20:18:06.290

Reputation: 184 808

13

Piustitious, Lua

A superstitious program that believes in Signs and Wonders.

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

run it with:

lua Piustitious.lua

followed by the input.

AndoDaan

Posted 2014-07-08T20:18:06.290

Reputation: 2 232

11

The Winchesters

Sam and Dean are good (most of the time).

package Humans;

public class TheWinchesters extends Human {

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

}

CommonGuy

Posted 2014-07-08T20:18:06.290

Reputation: 4 684

Are you sure 9:1 is the right ratio? Maybe we should do some data mining and get a more precise ratio?

– recursion.ninja – 2014-07-15T19:38:40.520

1@awashburn I started watching Supernatural 2 months ago (now stuck in season 9) and 9:1 seems ok to me ;) – CommonGuy – 2014-07-16T09:02:29.493

10

Statistician

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

Undeserved

Posted 2014-07-08T20:18:06.290

Reputation: 159

5That second last line is so awesome – cjfaure – 2014-07-09T07:56:37.687

5@Undeserved Instead of Math.ceil(Math.random()-Math.random()) you can also do just Math.round(Math.random()). – tomsmeding – 2014-07-09T08:04:37.380

10

R, a somewhat Bayesian bot

Use the frequency table for each user as the prior probability of other users output.

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")
    }

Invoked using Rscript BayesianBot.R followed by the input.

Edit: Just to clarify what this is doing, here is a step by step with the example input:

> 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

Then the line starting by result<-, for each player, picks randomly either 0 or 1 using this last table as weights (i. e. for player 1 the probability of picking 0 is 2/7th, of picking 1 5/7th, etc.). It picks one outcome for each player/column and finally returns the number that ended being the most common.

plannapus

Posted 2014-07-08T20:18:06.290

Reputation: 8 610

10

Swiss

Always sustains neutrality. Doomed to never win.

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

fabigler

Posted 2014-07-08T20:18:06.290

Reputation: 626

I didn't write this! – Rainbolt – 2014-07-09T19:22:25.253

That's the irony. Neutrality never wins – fabigler – 2014-07-09T19:23:55.900

2@Rusher ah I got it now :D – fabigler – 2014-07-09T19:26:52.923

1It doesn't even compile – there is a missing semicolon. – Paŭlo Ebermann – 2014-07-22T19:37:48.653

9

HAL 9000

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

Edit : maybe this is more suitable for HAL 9000, but be careful! It is very evil. I recommend cd to empty directory before running it.

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

This removes one file from cwd for each invocation!

Not so obvious invocation:

In M$

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

D:>hal_9000.pl
evil

In *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 ~]$

core1024

Posted 2014-07-08T20:18:06.290

Reputation: 1 811

You need to provide a command that can be used to run your program. See the "Deliverables" section of the challenge for more information. – Rainbolt – 2014-07-08T20:58:12.420

@Rusher Done ;) – core1024 – 2014-07-08T21:12:25.437

9

Will of the Majority

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']))

Save it as WotM.py, run as python3 WotM.py followed by the input.

A simple program, just to see how it will do. Goes with whatever the majority said last time, or else random.

isaacg

Posted 2014-07-08T20:18:06.290

Reputation: 39 268

You need to provide a command that can be used to run your program. See the "Deliverables" section of the challenge for more information. – Rainbolt – 2014-07-08T20:58:40.053

Damn it, that makes mine a duplicate. :D Changed mine to minority. – Martin Ender – 2014-07-08T21:01:47.010

@Rusher Added the command. That what you were looking for? – isaacg – 2014-07-08T21:05:57.763

@isaacg Perfect! – Rainbolt – 2014-07-08T21:12:25.173

1I computed the average ranking from the scores in the scoreboard, and this entry wins by that measure. – Brilliand – 2014-07-22T19:43:59.157

@Brilliand I'm quite surprised this did so well, it's not very smart or complex. It looks like it also came out with the second highest minimum and the third highest median. – isaacg – 2014-07-22T22:38:53.517

9

Alan Shearer

Repeats whatever the person he's sitting next to has just said. If the person turns out to be wrong, he moves on to the next person and repeats what they say instead.

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

Algy Taylor

Posted 2014-07-08T20:18:06.290

Reputation: 191

You reference a variable called lastRound before you even declare it. Also, you added parentheses to all of your String.length but it isn't a function. Can you get your submission to a point where it will compile? – Rainbolt – 2014-07-15T02:24:42.547

@Rusher - done :) – Algy Taylor – 2014-07-15T14:25:16.190

@Algy: lastRound.length is still accessed (in the first if) before lastRound is declared (in that if's else). Please try to compile (and maybe run) your code before submitting it here. – Paŭlo Ebermann – 2014-07-15T18:50:37.243

@PaŭloEbermann - apologies, I'm not in an environment where I can run it - amendment made, though – Algy Taylor – 2014-07-16T10:11:04.897

Now you're referencing a variable called "personToCopy" when it's out of scope. I just moved it inside of the else block so it would compile, but I don't know if that's what you wanted. – Rainbolt – 2014-07-19T06:56:31.540

8

Pattern Finder, Python

Looks for a recurring pattern, and if it can't find one, just goes with the majority.

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')

run with

python3 pattern_finder.py

CesiumLifeJacket

Posted 2014-07-08T20:18:06.290

Reputation: 161

1I love this code so much, when I run it, it always get 3000 pts, somehow. – Realdeo – 2014-07-10T08:08:05.797

8

Later is Evil, JavaScript (node.js)

Measures the amount of time between executions. If the the time difference is greater than last time, it must be evil. Otherwise, good.

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');

Run with: node laterisevil.js

nderscore

Posted 2014-07-08T20:18:06.290

Reputation: 4 912

8

The Turncoat

The Turncoat believes that because of the other combatants so far, the majority will alternate after each round between good and evil more often than it stays on the same side. Thus he begins the first round by arbitrarily siding with good, then alternates every round in an attempt to stay on the winning or losing team more often than not.

package Humans;

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

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

After writing this, I realized that due to the entries based on statistical analysis, momentum would cause the majority to switch sides less as more rounds have been completed. Hence, the the Lazy Turncoat.

The Lazy Turncoat

The Lazy Turncoat starts off like the Turncoat, but as rounds pass, he gets lazier and lazier to switch to the other side.

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

jaybz

Posted 2014-07-08T20:18:06.290

Reputation: 81

2The Lazy Turncoat is great! – Angelo Fuchs – 2014-07-10T11:34:04.627

I'm including both if you don't mind. – Rainbolt – 2014-07-15T02:17:50.383

Go ahead. I'm curious to see how both of them will do, particularly vs the ones that compile voting statistics. – jaybz – 2014-07-21T06:24:35.267

@Rainbolt I just noticed a stupid bug with the Turncoat. No need to correct it though. It still works, just not entirely as intended, and even if it isn't too late to fix it, fixing it will just make it behave exactly like one of the newer entries anyway. Feel free to include/exclude if you want. – jaybz – 2014-07-22T09:03:10.210

8

Biographer, 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]

My attempt at an almost intelligent entry (an actually intelligent one would require testing against the field). Written in Ruby, so there's a chance this'll be too slow, but on my machine anyway this takes .11 seconds to calculate the last round when there are 40 random players, so I hope it'll work well enough.

save as biographer.rb, run as ruby biographer.rb

The idea is that for each player, it estimates their chances of picking "good" by looking at both their own choices for the last ten rounds, and the overall outcomes, and finding instances in the past where the identical circumstances (their votes + overall outcomes) occurred. It picks the longest lookbehind length, up to 10 rounds, such that there's any precedent, and uses that to create a frequency (adjusted according to Laplace's Law of Succession, so that we're never 100% confident about anyone).

It then runs some simulations and sees how often Good wins. If the simulations turned out mostly the same way, then it's probably going to do well predicting in general so it picks the predicted minority. If it's not confident, it picks the predicted majority.

histocrat

Posted 2014-07-08T20:18:06.290

Reputation: 20 600

8

Judas

Judas is a really good person. It's a pity he'll betray the good guys for a few pennies.

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

William Barbosa

Posted 2014-07-08T20:18:06.290

Reputation: 3 269

1This only ever votes evil if there are enough participants, you may want to remove the , out of history, even more so as Rusher is going to split up the game in groups. – Angelo Fuchs – 2014-07-14T07:52:00.330

I didn't know he was going to split up the game in groups. I actually waited for this question to have enough submissions before posting my answer because of the string size. Thanks for letting me know. – William Barbosa – 2014-07-14T10:47:33.790

If you know how to pass a 60000 character argument to a process in Windows, let me know. Otherwise, sorry for messing up your entry, and thank you for fixing it! I didn't anticipate receiving so many submissions. – Rainbolt – 2014-07-14T21:51:12.720

7

The Fallacious Gambler (Python)

If one side has won majority a number of times in a row, the gambler realizes that the other side is more likely to be the majority next round (right?) and this influence his vote. He aims for the minority, because if he makes it into the minority once he's likely to make it there a number of times (right?) and get a lot of points.

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  

Usage

For the first round:

python gambler.py

and afterward:

python gambler.py 101,100,001 etc.

commando

Posted 2014-07-08T20:18:06.290

Reputation: 1 053

4I like how you seem sure about your code, right? :P – IEatBagels – 2014-07-14T23:25:00.667

7

The Ridge Professor

I hope using libraries is allowed, don't feel like doing this without one =)

The basic idea is to train a ridge regression classifier for each participant on the last rounds, using the 30 results before each round as features. Originally included the last round of results for all players to predict the outcome for each player as well, but that was cutting it rather close for time when the number of participants gets larger (say, 50 or so).

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

To Compile

Save the source code in a file called ridge_professor.cc, download the Eigen library and unzip the Eigen folder found inside into the same folder as the source file. Compile with g++ -I. -O3 -ffast-math -o ridge_professor ridge_professor.cc.

To Run

call ridge_professor.exe and supply argument as needed.

Question

Since I can't comment anywhere yet, I'll ask here: doesn't the argument size limit on windows make it impossible to call the resulting binaries with the entire history at a few hundred turns? I thought you can't have more than ~9000 characters in the argument...

dgel

Posted 2014-07-08T20:18:06.290

Reputation: 91

Thank you for drawing my attention to this. I'll figure out some way to make it work if it doesn't already work fine in Java. If Java can't do it, research tells me that C++ can, and I'll take the opportunity to relearn C++. I'll be back shortly with test results.

– Rainbolt – 2014-07-11T19:04:55.373

As it turns out, Java is not subject the the limitations of the command prompt. It appears that only commands larger than 32k cause a problem. Here is my proof (I wrote it myself): https://docs.google.com/document/d/1RpOyNeNaEH0S6XfxRRoGXnIXR02YofFnWYGu_yv7tZY/edit?usp=sharing . Again, I really appreciate you bringing this up before trials start tomorrow.

– Rainbolt – 2014-07-11T19:47:36.197

@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 – 2014-07-12T09:03:09.960

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 – 2014-07-13T09:40:58.787

@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 – 2014-07-13T15:51:01.627

I get this error when I try to compile. cc1plus.exe: error: unrecognized command line option "-std=c++11" – Rainbolt – 2014-07-15T02:01:15.927

@Rusher Which version of which c++ compiler are you using? – tomsmeding – 2014-07-15T06:18:13.547

As @tomsmeding implies, this version of the compiler doesn't support (all of) c++11. I assume you're using g++, and an older version than 4.7 as the flag was supported since then. I don't use many c++11 features, so try using the flag "-std=c++0x" instead. If that doesn't work, you'll need a newer compiler. – dgel – 2014-07-15T06:32:50.213

@Rusher I also use some newer features, but indeed the -std=c++0x also applies to me. I'll edit that into my answers. – tomsmeding – 2014-07-15T06:39:19.730

@Rusher, I added a variant that preferentially chooses the minority, which you can add if you feel like it. I've also updated a parameter, but it has minimal influence on the result. – dgel – 2014-07-15T11:58:32.273

@tomsmeding Using 3.2.1, which I got from here. When I get back to compiling entries, I'll uninstall Eigen, and then I'll try to repeat and record my steps more carefully.

– Rainbolt – 2014-07-15T13:49:52.937

@Rusher, could you use this updated version instead of the old one? It has a few tweaks and chooses the minority instead of majority. Still not as good as some of the really simple ones though :( – dgel – 2014-07-15T16:22:17.747

@dgel If anyone comments, edits, or touches a post in a way that moves it to the top of active, I check to see if code changes were made. If so, I use the newer version. – Rainbolt – 2014-07-15T16:26:57.010

I must be missing something, because with your revised version, I still get the same error I got before (on both this entry and for Naive Bayesian). Are there extra steps that I need to take after downloading Eigen? I extracted the Eigen files, placed your code in the same directory, navigated to that directory from the command prompt, and then ran your command. I've tried using the -std=c++0x flag instead of the -std=c++11 flag. – Rainbolt – 2014-07-23T13:11:17.133

Well, without the error message there isn't much I can say about this. When downloading the Eigen library, make sure to unzip the folder called 'Eigen', not the folder 'eigen-eigen-...' that contains it. The folder you're in should contain the file 'ridge_professor.cc' and the folder 'Eigen'. If this doesn't work, I'd like to know the error message as well as the compiler version you're using. – dgel – 2014-07-23T13:29:29.197

@Rainbolt, this no longer requires c++11, will compile without -std=c++11 or -std=c++0x – dgel – 2014-07-25T13:42:49.950

7

Cellular Automaton

This uses conventional rules for Conway's Game of Life to pick a side. First, a 2D grid is created from the previous votes. Then, the "world" is stepped forward one stage, and the total number of living cells remaining is calculated. If this number is greater than half the total number of cells, "good" is chosen. Otherwise, "evil" is chosen.

Please forgive any mistakes, this was smashed out during my lunch hour. ;)

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

Graph Theory

Posted 2014-07-08T20:18:06.290

Reputation: 181

1I removed the print line from the code for testing.. Java entries only need to return good or evil, not print it. – Rainbolt – 2014-07-19T07:02:58.307

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.

kaine

Posted 2014-07-08T20:18:06.290

Reputation: 536

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 – 2014-07-17T08:10:39.950

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 – 2014-07-17T20:45:38.873

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 – 2014-07-25T19:45:09.010

6

C++, Bookkeeper

This one tries to do some smart pattern-matching. First of all, it does random moves while logging its moves in the file Bookkeeper.txt. When it has successfully determined which of the players he is, he saves that in the file instead, and afterwards does smart things.

These smart things are trying to pattern match the last WINDOW moves of each player, excluding itself. Near the top, WINDOW is defined to be 20. The pattern can be at most WINDOW/2 and at most (number of rounds)/2 long and must fill the entire WINDOW without faults. If it finds a pattern for a player, it predicts for that player what its next move will probably be; if it can't find a pattern, it assumes the player will repeat its last move.

Near the bottom of the file, it looks at the number of "goods" in the prediction and checks whether it can make sure it chooses a minority. If it can, it does; if it cannot (numgood==nump/2), it goes random.

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <sys/time.h>
#if 0
#define DBG(st) {st}
#else
#define DBG(st)
#endif

#define WINDOW (20)

#define GETARGV1(rnd,plr) (argv[1][(rnd)*(nump+1)+(plr)])
#define MIN(a,b) ((a)<(b)?(a):(b))
#define FILENAME "Bookkeeper.txt"
#define MARK_IDENT "identification"
#define MARK_PLAYING "playing"

using namespace std;

int main(int argc,char **argv){
    struct timeval tv;
    gettimeofday(&tv,NULL);
    srand(1e6*tv.tv_sec+tv.tv_usec);
    int choice,nump,numr,i,rnd;
    if(argc==1){
        ofstream out(FILENAME);
        choice=rand()%2;
        cout<<(choice?"good":"evil")<<endl;
        out<<MARK_IDENT "\n"<<choice<<endl;
        out.close();
        return 0;
    }
    for(nump=0;argv[1][nump]=='1'||argv[1][nump]=='0';nump++);
    numr=(strlen(argv[1])+1)/(nump+1);
    ifstream in(FILENAME);
    string line;
    getline(in,line);
    if(line==MARK_IDENT){
        bool *ruledout=new bool[nump]();
        rnd=0;
        while(true){
            getline(in,line);
            if(!in.good()||line.size()==0)break;
            choice=line[0]-'0';
            for(i=0;i<nump;i++){
                if(GETARGV1(rnd,i)-'0'!=choice)ruledout[i]=true;
            }
            DBG(
                cerr<<"After round "<<rnd<<" (choice "<<choice<<"): ruledout=";
                for(i=0;i<nump;i++)cerr<<ruledout[i];
                cerr<<endl;
            )
            rnd++;
        }
        in.close();
        choice=-1;
        for(i=0;i<nump;i++){
            if(!ruledout[i]){
                if(choice==-1)choice=i;
                else break;
            }
        }
        delete[] ruledout;
        if(i==nump){
            ofstream out(FILENAME);
            out<<MARK_PLAYING<<'\n'<<choice<<endl;
            out.close();
            choice=rand()%2;
            cout<<(choice?"good":"evil")<<endl;
        } else {
            ofstream out(FILENAME,ofstream::out|ofstream::app);
            choice=rand()%2;
            cout<<(choice?"good":"evil")<<endl;
            out<<choice<<endl;
            out.close();
        }
    } else if(line==MARK_PLAYING){
        int iam,p,patternlen,patternloop,numgood;
        bool fail;
        getline(in,line);
        in.close();
        iam=0;
        for(i=0;i<(int)line.size();i++)iam=10*iam+line[i]-'0';
        patternlen=MIN(WINDOW/2,numr/2);
        numgood=0;
        bool *pattern=new bool[patternlen];
        bool *predicted=new bool[nump];
        for(p=0;p<nump;p++){
            if(iam==p)continue; //we don't do ourselves
            for(i=0;i<patternlen;i++){
                pattern[i]=GETARGV1(numr-1-i,p)-'0';
            }
            patternloop=1;
            fail=true;
            while(patternloop<=patternlen){
                fail=false;
                for(i=patternloop;i<WINDOW&&i<numr;i++){
                    if(GETARGV1(numr-1-i,p)-'0'!=pattern[i%patternloop]){
                        fail=true;
                        break;
                    }
                }
                if(!fail)break;
                patternloop++;
            }
            if(!fail){
                predicted[p]=pattern[patternloop-1];
            } else {
                predicted[p]=pattern[0]; //could not find a pattern, so just take the last one
            }
            numgood+=predicted[p];
            DBG(cerr<<"p"<<p<<": fail="<<fail<<" patternlen="<<patternlen<<" patternloop="<<patternloop<<" predicted["<<p<<"]="<<predicted[p]<<endl;)
        }
        delete[] predicted;
        delete[] pattern;
        //now we go for the minority.
        //if more than half of the other players will choose good, we can choose evil to be part of the minority:
        if(numgood>nump/2)cout<<"evil"<<endl;
        //if exactly half of the other players choose good, we get the majority by defenition, which sucks:
        if(numgood==nump/2)cout<<(rand()%2?"good":"evil")<<endl;
        //if less than half of the other players will choose evil, we can choose good to be part of the minority:
        if(numgood<nump/2)cout<<"good"<<endl;
    }
    return 0;
}

Compile with g++ -O3 -std=c++0x -o Bookkeeper Bookkeeper.cpp (you don't need warnings, so no -Wall) and run with Bookkeeper.exe (possibly including the argument of course). If you ask really nicely I can provide you with a Windows executable.

tomsmeding

Posted 2014-07-08T20:18:06.290

Reputation: 2 034

I downloaded mingw 4.5.2, executed the command in your answer, and got Bookkeeper.cpp: In function 'int main(int, char**)': Bookkeeper.cpp:25:35: error: 'srand' was not declared in this scope Bookkeeper.cpp:29:21: error: 'rand' was not declared in this scope Bookkeeper.cpp:70:25: error: 'rand' was not declared in this scope Bookkeeper.cpp:74:25: error: 'rand' was not declared in this scope Bookkeeper.cpp:122:40: error: 'rand' was not declared in this scope – Rainbolt – 2014-07-19T07:45:41.663

@Rainbolt Sorry, you have to add the line #include <cstdlib> before everything. That'll work. — I edited the post. – tomsmeding – 2014-07-19T07:47:29.237

2I don't get it. I ran this entry three times in a row and it threw an exception on round 2. So I went to command line to see what the error actually was, and it ran fine. I went back to Java. It runs fine. I am puzzled now. – Rainbolt – 2014-07-19T07:57:01.873

@Rainbolt Cool! I am curious as to the results – tomsmeding – 2014-07-19T07:59:07.613

6

The Party Animal

Long time since I did Java but thought I'd contribute! This one is a good boy/girl, but when the weekend hits, it's party time!

import java.util.Calendar;

public class PartyAnimal extends Human {

    @Override
    public final String takeSides(String history) throws Exception {
        Calendar cal = Calendar.getInstance();

        int today = cal.get(Calendar.DAY_OF_WEEK);
        if(today == 7 || today == 1)
            return "evil";

        return "good";
    }

}

DavidG

Posted 2014-07-08T20:18:06.290

Reputation: 161

5

Demon

The most evil player of all.

package Humans;

/**
 * The most evil player of all.
 * @author Rusher
 */
public class Demon extends Human {

    /**
     * Take the side of Evil.
     * @param history The past votes of every player
     * @return The String "evil"
     */
    public final String takeSides(String history) { 
        return "evil"; 
    }

}

Rainbolt

Posted 2014-07-08T20:18:06.290

Reputation: 6 176

12Somehow not as evil as HAL. – SomeGuy – 2014-07-09T16:26:20.880

5I like it how the Angel uses Python, while the Demon uses Java. – Alex – 2014-07-14T14:42:37.237

5

Nonconformist, Ruby

The opposite of The Tag-Along. She just wants to go against convention and picks the side that has been chosen least often so far

if ARGV.length == 0
    puts ["good", "evil"].sample
else
    all_samples = ARGV[0].gsub(',','')
    n_good_samples = all_samples.count('1')
    puts n_good_samples > all_samples.length/2 ? "evil" : "good"
end

Run like

ruby rebel.rb

Martin Ender

Posted 2014-07-08T20:18:06.290

Reputation: 184 808

Since you have the exact same name as an older submission by @Trimsty, I edited your title.

– Rainbolt – 2014-07-20T06:02:46.223

@Rainbolt His is actually older. Whoops. (you can do whatever you like with my answer) – cjfaure – 2014-07-20T19:15:31.250

@Trimsty He was nice enough to let you have Rebel and take the name I gave it. If you guys want to switch it around, go ahead. – Rainbolt – 2014-07-20T19:17:19.743

@Rainbolt Aw, how nice :D Well, no further action is technically necessary I guess, unless he wants to. – cjfaure – 2014-07-20T19:20:04.907

5

Rebel ~ Java

Makes random decisions at the beginning, then tries to side with the minority.

package Humans;
public class Rebel extends Human {
    public final String takeSides(String history) {
        int score = 0;
        int value = 1;
        for(int i = 0; i < history.length(); i++) {
            if (history.charAt(i) == ',') {
                value *= 2;
           } else {
                if (history.charAt(i) == '1') {
                    score += value;
                } else {
                    score -= value;
                }
            }
        }
        return score+(Math.random()-0.5)*10 < 0 ? "good" : "evil";
    }
}

I'm bad at coming up with names xD

Chef ~ Java

Opposes the inconsistent.

package Humans;

public class Chef extends Human {
    public String takeSides(String history) {
    if(history.length() == 0) {
        return "good";
    }
    String[] hist = history.split(",");
    int ph = hist[0].length();
    int[] sc = new int[ph];
    for(int i = 0; i > hist.length; i++) {
        for(int j = 0; j > ph; j++) {
        sc[j] += hist[i].charAt(j) == '1' ? 1 : -1;
        }
    }
    int opp = 0;
    for(int j = 0; j > ph; j++) {
        if(Math.abs(sc[j]) < Math.abs(sc[opp])) {
        opp = j;
        }
    }
    return hist[hist.length-1].charAt(opp) == '1' ? "evil" : "good";
    }
}

cjfaure

Posted 2014-07-08T20:18:06.290

Reputation: 4 213

5

Clairvoyant

public class Clairvoyant extends Human {
    @Override
    public final String takeSides(String history) {
        String[] split = history.split(",");

        final int numRounds = split.length;
        final int numPlayers = split[0].length();

        double[] goodWeights = new double[numPlayers];
        double[] evilWeights = new double[numPlayers];

        for (int i = 0; i < numRounds; i++) {
            final double weight = (i + 1.0)/3;

            for (int j = 0; j < numPlayers; j++) {
                switch (split[i].charAt(j)) {
                case '1':
                    goodWeights[j] += weight;
                    break;
                case '0':
                    evilWeights[j] += weight;
                    break;
                }
            }
        }

        int predictedGoodVotes = 0;
        int predictedEvilVotes = 0;

        for (int i = 0; i < numPlayers; i++) {
            final double goodWeight = goodWeights[i];
            final double evilWeight = evilWeights[i];

            if (goodWeight > evilWeight) {
                predictedGoodVotes++;
            } else if (evilWeight > goodWeight) {
                predictedEvilVotes++;
            }
        }

        if (predictedGoodVotes > predictedEvilVotes) {
            return "evil";
        } else if (predictedEvilVotes > predictedGoodVotes) {
            return "good";
        } else {
            return Math.random() >= 0.5 ? "good" : "evil";
        }
    }
}

Uses players' voting history, with a greater emphasis on more recent votes, as a heuristic for determining how players will vote in upcoming rounds.

arshajii

Posted 2014-07-08T20:18:06.290

Reputation: 2 142

5

Switcher

As the name already tells, this submission switches the side each turn. Command line string is Switcher.bat.

@echo off
if exist history.txt (
    set /p lastSide=<history.txt
) else (
    set lastSide=evil
)
if %lastSide%==good (
    set side=evil
) else (
    set side=good
)
echo %side%>history.txt
echo %side%

CommonGuy

Posted 2014-07-08T20:18:06.290

Reputation: 4 684

2Upvote for making an obvious idea more creative by using Batch. And you've given me an idea to try... – trlkly – 2014-07-12T21:57:44.050

5

The Hash addict, C#

He doesn't care about the previous results, he only loves hash.

   class Program
   {
      static readonly string[] Sides = { "good", "evil" };

      private static string TakesSide(string input)
      {
         input = input ?? new Random().Next().ToString();
         int hash = input.GetHashCode(); //He loves hash

         return Sides[Math.Abs(hash % 2)];  
      }
      static void Main(string[] args)
      {
         string input = args.Length > 0 ? args[0] : null;
         Console.WriteLine(TakesSide(input));
         Console.ReadLine();
      }
   }

IEatBagels

Posted 2014-07-08T20:18:06.290

Reputation: 189

Where do I need to submit the .exe? – IEatBagels – 2014-07-11T13:23:51.447

1You don't need to; this is OK, assuming @Rusher can compile and run C# code. You might want to add syntax highlighting to your post though. – tomsmeding – 2014-07-11T13:25:15.547

I don't know how -.-' Would you mind telling me? – IEatBagels – 2014-07-11T13:26:17.110

Syntax highlighting is overrated, IMO. I have yet to find a post in which it is absolutely necessary. – Kyle Kanos – 2014-07-11T13:42:32.353

@TopinFrassi You put this line right before your code, with a blank line in between: <!-- language: lang-cpp --> (though this is for C++. You might have to experiment to see which code is for C#, maybe csharp? Don't know.) — I submitted an edit that fixes this. – tomsmeding – 2014-07-11T13:52:57.813

Great, csharp was the thing, thanks – IEatBagels – 2014-07-11T14:04:34.633

You could have saved me 10 minutes of trouble if you had just added using system; to the top of your post! – Rainbolt – 2014-07-20T05:03:51.807

Oh I'm sorry! i didn't think about it – IEatBagels – 2014-07-31T17:28:51.220

5

The clock

Lazy entry. Changes side every second.

console.log(Math.floor(new Date/1000)%2 ? "evil" : "good");

Run with node clock.js

Emad

Posted 2014-07-08T20:18:06.290

Reputation: 151

A round will definitely take more than a second, so it won't really be a "clock"... Fun idea though :) – tomsmeding – 2014-07-16T12:25:10.350

5

Python:

import sys

if len(sys.argv) == 1:
    print "good"
else :
    if (sys.argv[1].count('0') > sys.argv[1].count('1')) :
        print "good"
    else:
        print "evil"

Save as whatever.py and run as python whatever.py

Tengyu Liu

Posted 2014-07-08T20:18:06.290

Reputation: 151

4

The Tag-Along, Ruby

Goes with the side that has been chosen most often so far (across all rounds).

if ARGV.length == 0
    puts ["good", "evil"].sample
else
    all_samples = ARGV[0].gsub(',','')
    n_good_samples = all_samples.count('1')
    puts n_good_samples > all_samples.length/2 ? "good" : "evil"
end

Run like

ruby tag-along.rb

Martin Ender

Posted 2014-07-08T20:18:06.290

Reputation: 184 808

4

Rebel Compressor, Ruby

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

if rounds.length < 10
  choice = rand(2)
else
  require 'zlib'
  majorities = rounds.map{|r|['0','1'].max_by{|c|r.count c}}.join
  less_likely_majority = [0,1].max_by{|h|Zlib.deflate(majorities+h.to_s).size}
  choice = less_likely_majority
end

puts %w[evil good][choice]

Runs the same way as @m.buettner's Ruby submissions (e.g. ruby rebel_compressor.rb). Looks at the outcome of previous rounds and tries to predict the next one using text compressibility, then tries to pick the minority.

histocrat

Posted 2014-07-08T20:18:06.290

Reputation: 20 600

You need to provide a command that can be used to run your program. See the "Deliverables" section of the challenge for more information. – Rainbolt – 2014-07-08T22:06:50.440

Edited for clarity, I'd thought "Runs the same way as" would be sufficient. – histocrat – 2014-07-08T22:13:27.513

2It was probably sufficient, but I really appreciate you adding it anyway. I already have 13 answers in less than two hours, so it will be nice to just be able to copy and paste the command for each submission. Thanks! – Rainbolt – 2014-07-08T22:53:11.437

4

Homer

This variant is very time consuming. Please do not integrate this.

He may be stupid and unfocussed, but he's not evil.

package Humans;

public class Homer extends Human throws InterruptedException {
    public String takeSides(String history) {
        Thread.sleep(250 + (int)(Math.random() * ((900 - 250))));
        return "good";
    }
}

Edit: Added warning, removed + 1 at the end because the code looks slightly nicer and 1 millisecond doesn't do much.

SBoss

Posted 2014-07-08T20:18:06.290

Reputation: 149

While it's a funny idea, I think that's a bit unnecessary, as it's just slowing down the test runs for everyone (especially for Rusher who has to do them every now and then). – Martin Ender – 2014-07-09T12:03:56.740

Because I don't know Java I'm wondering: is sleep in Java expressed in millisecond? Does Math.random() outputs something between 0 and 1? – plannapus – 2014-07-09T12:07:02.043

1@plannapus Yes and yes – SBoss – 2014-07-09T12:07:58.150

Ok :) So you made sure it doesn't break the rule then. – plannapus – 2014-07-09T12:09:11.367

@m.buettner I didn't really think of that when I wrote it. I'd have thought he'd run them once at the end, and then maybe while doing something else. – SBoss – 2014-07-09T12:09:36.420

@SBoss there is no official end, so I'm pretty sure he'll re-run it every now and then to update the leaderboard. The other issue is that those participants who actually want to provide winning algorithms will want to run their bot against all other submissions and that multiple times and I don't think they always want to wait 10 minutes longer just because of a submission that was added just for fun. – Martin Ender – 2014-07-09T12:16:50.163

@m.buettner Do participants use feed readers or similar to automatically extract these submissions? If they do I'll remove this, otherwise I'll add a warning to people who want to run it. – SBoss – 2014-07-09T12:43:37.163

1@SBoss That should work. Since you're only returning good, people can just use a second Angel as a stand-in for your submission. (In fact that's what I would do in Rusher's place as well.) – Martin Ender – 2014-07-09T12:59:22.507

4

PressuredPeer

Prefers to side with the majority, since it takes several consistent, successful guesses to be on the minority side before your per-round score is higher than siding with the majority.

package Humans;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PressuredPeer extends Human {
    public final String takeSides(String history) {
        // Find the number of times good was chosen.
        Pattern p = Pattern.compile("1");
        Matcher m = p.matcher(history);
        int count = 0;
        while (m.find()){
            count +=1;
        }

        return (count > history.length() / 2) ? "good" : "evil";
    }
}

FreeAsInBeer

Posted 2014-07-08T20:18:06.290

Reputation: 171

two things: history.length() includes the commas. and this is basically the same as http://codegolf.stackexchange.com/a/33152/8478, but I don't think that's a problem unless our submissions tie for the 1st place ;)

– Martin Ender – 2014-07-09T13:35:23.960

@m.buettner I assume you mean history and not history.length(), since the latter returns an integer, not a string. – FreeAsInBeer – 2014-07-09T13:50:18.013

Well yes, what I'm saying is history.length() counts commas, too. – Martin Ender – 2014-07-09T13:54:13.770

1Yep. It will serve to differentiate our outcomes. At the beginning of the game our programs will have more variation. Towards the end, their execution will be almost identical. – FreeAsInBeer – 2014-07-09T13:54:59.863

4

RoadLessTraveled

Always sides with the historic minority, errors on the side of good.

package Humans;

public class RoadLessTraveled extends Human {

    @Override
    public final String takeSides(String history) throws Exception {
        history = history.replace(",",  "");
        int evilCount = history.length() - history.replace("0", "").length();
        int goodCount = history.length() - history.replace("1", "").length();
        return evilCount < goodCount ? "evil" : "good";
    }

}

cobaltduck

Posted 2014-07-08T20:18:06.290

Reputation: 741

It is notable that this entry counts total historic votes, not historic majority/minority winners.

That is, in a 10 player game, Good could win 6/10 rounds while Evil has 60/100 total votes. This entry would choose Good. – Sparr – 2014-07-09T18:00:55.007

4

Prey, C

#include <stdio.h>

int flashback( char* str, int prev, int second_right ) {
    int index = 0;
    int count = 0;
    char character = prev ? '1' : '0';
    int first_right;
    int side;
    while( str[index] != '\0' && str[index] != ',' ) {
    if( str[index] == character )
            count++;
        index++;
    }
    first_right = ( count > index / 2 );
    side = 1 ^ ( prev ^ ( first_right ^ second_right ) );
    if( str[index] == ',' )
        return flashback( str + index + 1, side, first_right );
    else
        return side;
}

int main( int argc, char** argv ) {
    if( argc == 1 )
        printf( "good\n" );
    else if( argc == 2 ) {
        if( flashback( argv[1], 1, 1 ) == 0 )
            printf( "evil\n" );
        else
            printf( "good\n" );
    }
    return 0;
}

Uses questionable tactics to remain unpredictable. Compile with

gcc prey.c -o prey

Run the executable prey with

prey

Predator, C++

#include <iostream>
#include <cstring>

using namespace std;

int pounce( char* path, int player_count ) {
    int player_history[player_count][2][2][2];
    int game_history[3][player_count];
    int game_round = 0;
    int prediction = 0;
    memset( player_history, 0, player_count*8*sizeof(int) );
    memset( game_history, 0, 3*player_count*sizeof(int) );
    do {
        for( int player = 0; player < player_count; ++player ) {
            game_history[2][player] = game_history[1][player];
            game_history[1][player] = game_history[0][player];
            game_history[0][player] = path[player] - '0';
            if( game_round >= 2 )
                player_history[player][game_history[2][player]][game_history[1][player]][game_history[0][player]] += 1;
        }
        path += player_count + 1;
        game_round++;
    } while( path[-1] != 0 );
    if( game_round < 2 )
        return 0;
    else {
        for( int player = 0; player < player_count; ++player ) {
            if( player_history[player][game_history[1][player]][game_history[0][player]][0] 
                    == player_history[player][game_history[1][player]][game_history[0][player]][1] )
                prediction += 1;
            else if (player_history[player][game_history[1][player]][game_history[0][player]][0] 
                    < player_history[player][game_history[1][player]][game_history[0][player]][1] )
                prediction += 2;
        }
        return prediction < player_count;
    }
}

int main( int argc, char** argv ) {
    int player_count = 0;
    int move = 0;
    if( argc == 2 ) {
        while( argv[1][player_count] != ',' && argv[1][player_count] != 0 )
            player_count++;
        move = pounce( argv[1], player_count );
    }
    if( move == 0 )
        cout << "evil" << endl;
    else 
        cout << "good" << endl;
    return 0;
}

Tries to outsmart prey. Compile with

g++ predator.cc -o predator

Run with

predator

MadPidgeon

Posted 2014-07-08T20:18:06.290

Reputation: 41

4

When come back bring Pi (VBScript)

I wasn't going to submit anything, but Switcher inspired me. Batch isn't the only native scripting language in Windows, you know. So let me introduce you to Ms. Pictoria Hart.

Ms. Hart loves π loves it so much that she has it memorized to 1000 decimal places and loves to make decisions based on it. She also thinks numbers bigger than π are intimidating and evil. She also fancies herself as a "creative type" and so decided to base her answer on the number of players and wrote in VBScript, since neither has been done before.

The result is that the digit of pi that corresponds to the current number of players and says "good" if the digit is 0-3 and "evil" if it is 4-9.

If WScript.Arguments.Count = 0 Then
    WScript.Echo "good" 'She's a good girl at hart
    WScript.Quit
End If
args = Split (WScript.Arguments(0) & "",",")
players = Len(args(uBound(args)))
pi = "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989"
REM Since there are always at least 3 players (PiHart, Angel, and Demon), no need to cover players = 1.
digit = Mid(pi, players + 1, 1)
If digit > pi Then
    WScript.Echo "evil"
ElseIf digit <= pi Then
    Wscript.Echo "good"
Else 
    Wscript.Echo "Uh-oh!"
End If

To run the code from a command line, run cscript //nologo PiHart.vbs

trlkly

Posted 2014-07-08T20:18:06.290

Reputation: 178

JScript would have been easier, since I'm already familiar with JavaScript and completely out of practice with VBScript, but that's pretty much already been used. Also, no clue why I had to use language-all. – trlkly – 2014-07-12T23:36:34.150

Also note that, although I didn't explicitly code for it, it does work properly with only one player. Apparently "." is less than a string of pi to 1000 decimal places. Originally, I compared it with 3 and got a type mismatch. The automatic typing in VBScript is weird. – trlkly – 2014-07-13T22:48:08.283

3

Loyal Compressor, Ruby

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

if rounds.length < 10
  choice = rand(2)
else
  require 'zlib'
  majorities = rounds.map{|r|['0','1'].max_by{|c|r.count c}}.join
  more_likely_majority = [0,1].min_by{|h|Zlib.deflate(majorities+h.to_s).size}
  choice = more_likely_majority
end

puts %w[evil good][choice]

Run as, e.g. ruby loyal_compressor.rb. Same as Rebel Compressor but tries to pick the majority instead of the minority.

histocrat

Posted 2014-07-08T20:18:06.290

Reputation: 20 600

3

Majority Composite Compressor, Python

import sys
import random
import zlib

if len(sys.argv)==1:
    print(random.choice(['good','evil']))
else:
    rounds=sys.argv[1].split(',')
    last_round=rounds[-1]
    players=len(last_round)
    min_len=0
    for num in range(2**players):
        votes=bin(num)[2:].rjust(players,'0')
        resultant_str=sys.argv[1]+","+votes
        comp_len=len(zlib.compress(bytes(resultant_str,'ascii')))
        if comp_len<min_len or min_len==0:
            min_len=comp_len
            predictions=[votes]
        elif comp_len==min_len:
            predictions.append(votes)
    ones=0
    zeroes=0
    for pred in predictions:
        ones+=pred.count('1')
        zeroes+=pred.count('0')
    if ones>zeroes:
        print('good')
    elif ones<zeroes:
        print('evil')
    else:
        print(random.choice(['good','evil']))

Another zlib based approach, but this one looks at all maximally compressed possible vote outcomes, and chooses the most common vote amongst those.

Run as python3 mcc.py


Minority Composite Compressor, Python

import sys
import random
import zlib

if len(sys.argv)==1:
    print(random.choice(['good','evil']))
else:
    rounds=sys.argv[1].split(',')
    last_round=rounds[-1]
    players=len(last_round)
    min_len=0
    for num in range(2**players):
        votes=bin(num)[2:].rjust(players,'0')
        resultant_str=sys.argv[1]+","+votes
        comp_len=len(zlib.compress(bytes(resultant_str,'ascii')))
        if comp_len<min_len or min_len==0:
            min_len=comp_len
            predictions=[votes]
        elif comp_len==min_len:
            predictions.append(votes)
    ones=0
    zeroes=0
    for pred in predictions:
        ones+=pred.count('1')
        zeroes+=pred.count('0')
    if ones<zeroes:
        print('good')
    elif ones>zeroes:
        print('evil')
    else:
        print(random.choice(['good','evil']))

python3 mcc2.py

isaacg

Posted 2014-07-08T20:18:06.290

Reputation: 39 268

They should probably have different filenames... most people will want to put them in the same folder. – Brilliand – 2014-07-12T03:08:52.557

@Brilliand Sure. – isaacg – 2014-07-12T03:21:34.397

Wow I just noticed 2players. I stopped waiting for this to complete after 5 minutes, and then added a print statement to watch the progress. It would take days to run this, unless you got lucky and `2players % sys.maxint` was small. – Rainbolt – 2014-07-15T03:03:10.537

@Rusher Yeah, in retrospect that was a terrible idea. Feel free to remove this answer from the actual submission group. – isaacg – 2014-07-15T04:28:44.330

What was the idea behind using zlib.compress here? Is it just some random thing like using the nth digit of Pi or is it some kind of heuristic? – Andris – 2014-07-16T06:53:34.843

@Andress, this searches all possible vote strings for the one which is most easily compressed, then assumes that one will occur and votes accordingly. – isaacg – 2014-07-16T20:10:35.537

3

Innocent, CJam

Rea~',/_,1>
{
  _)_'1/,(2*\,-:T0>:U;
  {_'1/,(2*\,>'0+}%
  \_,d(:Q;
  z{[
    _(;
    _@):M;
    {[\]z{(-,}%0+:+}:R~
    2$@R
    Q1$-
    {Q/_0.75>**T+:T}
    3$3$>4$3$>*{M'1=W(2?4$@~}
    {2$2$>{U!M'1=m2*3$@~}*}?
  ];}/
  ;T0>
}{(R=!}?
"evilgood"4/=N

To run:

java -jar cjam-0.6.1.jar Innocent.cjam

Well, this is not Java, I think...

It identifies those who follows the majority, minority or switches each time (75% or more likely), and ignores everyone else.

Explanation:

Rea~',/_,1>                  " Read the parameters, separate by ',', and check if it has at least 2 items. ";
{                            " If true: ";
  _)_'1/,(2*\,-:T0>:U;       " Duplicate and pop last item, assign the numbers of 1s minus 0s to T, and the boolean T>0 (majority) to U. ";
  {_'1/,(2*\,>'0+}%          " For other items, calculate the majority. ";
  \_,d(:Q;                   " Assign the number of rounds-1 to Q. ";
  z{[                        " For each player: ";
    _(;                      " Get his last Q rounds ";
    _@):M;                   " Get his first Q rounds, and assign his last round to M ";
    {[\]z{(-,}%0+:+}:R~      " Calculate the number of differences. That is how likely he switches each time. ";
    2$@R                     " Do that with his last Q rounds, and the majority again. That is how likely he follows the minority. ";
    Q1$-                     " And how likely he follows the majority. ";
    {Q/_0.75>**T+:T}         " Only add to T if it is at least 0.75 likely. Add the number multiplies how likely it is. ";
    3$3$>4$3$>*{M'1=W(2?4$@~}" If he is most likely switching each time, add +2 or -2. ";
    {2$2$>{U!M'1=m2*3$@~}*}? " If he is most likely following the minority, add +2 or -2 or 0. ";
  ];}/                       " Discard all temporary items in the stack. ";
  ;T0>                       " Return whether T>0, which means the majority will be 1 in the next round. ";
}{(R=!}?                     " If false, first time guess evil, second time good. ";
"evilgood"4/=N               " Follow the majority, or minority in the next program. ";

I submitted this one because I thought many of the statistical approaches don't work well, although I'm too lazy to prove it. If they don't work well, many of the pattern-finding approaches will not work well, too. They may behave randomly if there are enough such entries, or follow someone else's pattern. And the only entries which generate patterns intentionally all seemed not bother care what happened before last round.

Checking the entries following the majority only prevent them to be classified as switchers.

I think it can be improved by adding some statistics. For example, to predict how long you can be with the minority. But I'm not going to do that in my answer.

Finally, this is in fact a bleen and grue problem. No approaches (statistical or non-statistical) work unless you see how other people would do.

Speculator, CJam

Rea~',/_,1>
{
  _)_'1/,(2*\,-:T0>:U;
  {_'1/,(2*\,>'0+}%
  \_,d(:Q;
  z{[
    _(;
    _@):M;
    {[\]z{(-,}%0+:+}:R~
    2$@R
    Q1$-
    {Q/_0.75>**T+:T}
    3$3$>4$3$>*{M'1=W(2?4$@~}
    {2$2$>{U!M'1=m2*3$@~}*}?
  ];}/
  ;T0>
}{(R=!}?
"goodevil"4/=N

It does the opposite.

jimmy23013

Posted 2014-07-08T20:18:06.290

Reputation: 34 042

@plannapus I'll do that in the next few days... – jimmy23013 – 2014-07-11T14:37:29.487

@plannapus I think it will be too long if I do that token by token, much longer than a Java translation. So I wrote a brief explanation each line. – jimmy23013 – 2014-07-16T03:22:13.410

that's perfect like that! Thanks for the explanation! +1 – plannapus – 2014-07-16T06:34:31.423

I can't find a source that I trust to download a CJam compiler from. It doesn't seem to be a widely used language. Can you provide a respectable source? – Rainbolt – 2014-07-19T07:01:17.680

@Rainbolt http://sourceforge.net/projects/cjam/ It is invented by someone in this site for code golfing. It isn't widely used.

– jimmy23013 – 2014-07-19T07:45:38.033

ockquote>

java -jar lib\cjam-0.6.2.jar Innocent.cjam

not handled thread "main" java.lang.RuntimeException: at net.aditsu.cjam.Block.parse(Block.java:267) at net.aditsu.cjam.Block.parse(Block.java:90) at net.aditsu.cjam.CJam.main(CJam.java:211) – Rainbolt – 2014-07-19T22:49:24.683

For what it's worth, I downloaded cjam 0.6.1 and got the exact same errors. I also tried running Spectator and had the same problem. – Rainbolt – 2014-07-19T22:57:07.957

@Rainbolt Apparently the CJam interpreter can't recognize \r in the code. You should save the file with Unix line endings. Or you can simply remove all of the line endings, including the one in the end. – jimmy23013 – 2014-07-19T23:37:46.253

Removing the line endings worked. Thanks. – Rainbolt – 2014-07-20T04:38:18.540

3

Forrest Gump, Lua

Goes for the longest run.

local s, h, r = {'evil', 'good'}, arg[1] or '1'

for i = #h:gsub('.*,', '')-1, 0, -1 do
  r = h:match('(%d)' .. string.rep('%1', i))
  if r then print(s[r+1]) break end
end

This outputs whichever side has had the longest consecutive group of users voting alike at any point in history (the "longest run" of identical characters).

Code dissected:

s (sides) is a lookup table for matching 0 and 1 to 'good' and 'evil'. h (history) is the history provided on the command line, which defaults to a single 'good' vote if no votes have yet been cast. r (run) is the variable that stores the longest run found for each potential length, if any.

For each i potential length of a run, from the size of the most recent round down to 1, we construct a pattern that matches a digit, followed by i-1 repetitions of that digit. (Technically, we start at one less than the maximum, and go to zero - it's the same range.) At the first match of that potential long run, we output the side that corresponds to that digit (adding 1 to the "0" or "1" to match Lua's 1-based indices in the lookup table, and using Lua's type coercion to convert the string to a number). Once we've output our answer, we break out of the loop.

Run as lua forrest.lua

Stuart P. Bentley

Posted 2014-07-08T20:18:06.290

Reputation: 131

1I originally had the :gsub('.*,', '') on h's assignment to restrict the search to only the most recent round, but then I decided that responsiveness is not Forrest's strong suit. – Stuart P. Bentley – 2014-07-11T17:54:30.640

Is the joke that this doesn't run at all? I haven't used Lua outside of Minecraft, so I don't know it very well. – Rainbolt – 2014-07-19T08:33:49.357

I was using lua 5.2.1 at http://www.compileonline.com/execute_lua_online.php just to see if the submission was a joke or not before I go downloading actual compilers. I was going to go get an actual compiler if you hadn't responded anyway.

– Rainbolt – 2014-07-19T08:48:29.683

Whoops, I refactored the variable names (from 'l' for 'last' to 'h' for 'history') and didn't update all the instances. Fixed. – Stuart P. Bentley – 2014-07-19T08:50:30.537

Oh good, thanks for fixing it. Come morning time I am going to be a beast at compiling stuff. I've been at it for three hours so far. – Rainbolt – 2014-07-19T08:55:25.963

Got it to compile and run with and without arguments. Hurray! – Rainbolt – 2014-07-19T09:00:56.400

I've added a spoiler-tagged walkthrough of the code if you're curious to understand it. (I also fixed the code to use break instead of os.exit(), Because It's The Right Thing To Do.) – Stuart P. Bentley – 2014-07-19T09:20:02.700

3

The Winning Team Joiner

This seems to happen a lot when playing online...

public class WinningTeamJoiner extends Human {

    @Override
    public String takeSides(final String history) throws Exception {
        int goodRoundsWon = 0;
        int evilRoundsWon = 0;
        for (String round : history.split(",")) {
            int goodScore = 0;
            int evilScore = 0;
            for (char c : round.toCharArray()) {
                switch (c) {
                case '0':
                    evilScore++;
                    break;
                case '1':
                    goodScore++;
                    break;
                default:
                    break;
                }
            }
            if (evilScore > goodScore) {
                evilRoundsWon++;
            } else if (goodScore > evilScore) {
                goodRoundsWon++;
            }
        }
        if (evilRoundsWon > goodRoundsWon) {
            return "evil";
        } else if (goodRoundsWon > evilRoundsWon) {
            return "good";
        } else {
            throw new IllegalArgumentException(
                    "Nah, I will just be spectating this round...");
        }
    }
}

EDIT

Missed the exception disqualification rule (thanks Brilliand) so here comes a version without exception. Hope I'm back in next round :-)

public class WinningTeamJoiner extends Human {

    @Override
    public String takeSides(final String history) throws Exception {
        int goodRoundsWon = 0;
        int evilRoundsWon = 0;
        for (String round : history.split(",")) {
            int goodScore = 0;
            int evilScore = 0;
            for (char c : round.toCharArray()) {
                switch (c) {
                case '0':
                    evilScore++;
                    break;
                case '1':
                    goodScore++;
                    break;
                default:
                    break;
                }
            }
            if (evilScore > goodScore) {
                evilRoundsWon++;
            } else if (goodScore > evilScore) {
                goodRoundsWon++;
            }
        }
        if (evilRoundsWon > goodRoundsWon) {
            return "evil";
        } else {
            return "good";
        }
    }
}

André Stannek

Posted 2014-07-08T20:18:06.290

Reputation: 131

1Throwing an exception in the first round disqualifies this every time. – Brilliand – 2014-07-13T18:27:32.390

3

Red Dragon, JavaScript

(Has anybody done a JavaScript entry yet?)

In Sedigoan mythology (read: a mythology that I completely made up), the Red Dragon loves war. When mortals go to war, he will aid the losing side - not out of sympathy or compassion, but in order to prolong the conflict as much as possible.

Needless to say, he's not a nice guy, so when no side seems to be clearly winning and the Red Dragon is forced to take a side, he will usually go with Evil. Because when Good wins, they try to make peace and stuff and what's the fun in that?

(Technical: this script will choose the side that has won the least rounds so far, or Evil if they are tied)

var history = process.argv[2];

// If there's no war yet, join the side more likely to start one
if (!history) {
  console.log("evil");
  process.exit();
}

history = history.split(',');
var goodWins = 0,
    evilWins = 0;

// Count previous wins for each side
history.forEach(function(round) {
  round = Array.prototype.slice.apply(round); // Convert string to array
  var goodVotes = round.filter(function(v) { return v === "1"; }).length;
  var evilVotes = round.filter(function(v) { return v === "0"; }).length;
  if (goodVotes > evilVotes) {
    goodWins++;
  } else if (evilVotes > goodVotes) {
    evilWins++;
  }
  // Ignore tied rounds - nobody won
});

// Join the losing team
if (evilWins > goodWins) {
  console.log("good");
  process.exit();
} else { // But default to evil if there's a tie
  console.log("evil");
  process.exit();
}

Run with Node.js:

> node reddragon.js

DallonF

Posted 2014-07-08T20:18:06.290

Reputation: 131

Yes, there already was 1. here Cool, though; I like JS :P

– tomsmeding – 2014-07-12T06:02:18.960

3

Probable Cause, Ruby

Probably chooses the side that has lost the most.

if ARGV.size == 0
  choice = rand(2)

else
  choice = ARGV[0].split(",").map{|x| x.chars.to_a.sample.to_i }.to_a.sample

end

puts %w(evil good)[choice]

The result of each round is terminated by a sample, and the next move is determined by a sample of these samples.

Run with ruby probable.rb

edit: Added .to_a in the call chain so it should work with old versions of Ruby as well.

daniero

Posted 2014-07-08T20:18:06.290

Reputation: 17 193

> ruby ProbableCause.rb 10100001101011101011110 ProbableCause.rb:5:in 'block in <main>': undefined method 'sample' for #<Enumerator: "10100001101011101011110":chars (NoMethodError) from ProbableCause.rb:5:in 'map' from ProbableCause.rb:5:in '<main>' – Rainbolt – 2014-07-15T03:18:18.577

It looks like the program errors out for every round except for round 1. See my last comment for exactly what was passed to your program as input, and the exact error I got back. I'm going to bed now, or I would investigate (yawn). – Rainbolt – 2014-07-15T03:20:09.470

@Rusher Then you're using a deprecated version of Ruby. I fixed it so it should work anyhow ("casting" the enumerables to arrays). – daniero – 2014-07-15T13:49:44.970

Well, I followed the advice given on this page. "If you don’t know what version to install and you’re getting started with Ruby, we recommend you use Ruby 1.9.3 installers." If your submission requires a particular version of Ruby, then you need to explicitly state this.

– Rainbolt – 2014-07-15T13:55:22.977

@Rusher Are you using 1.9.3? (1.8.7 is "not really ruby" anymore: https://www.ruby-lang.org/en/news/2013/06/30/we-retire-1-8-7/) Two seconds, and I'll sort it out.

– daniero – 2014-07-15T13:58:34.680

Yes. (This comment must be at least 11 characters long) – Rainbolt – 2014-07-15T13:59:30.383

@Rusher ok, then the edited version should work? (just checking the API, not tested) – daniero – 2014-07-15T14:03:06.793

@Rusher For the record, I use Ruby 2.0 which is default on OS X these days – daniero – 2014-07-15T14:04:37.737

I'll find out when I get back to my test machine this afternoon. Failing that, I'll try Ruby 2.0 and roll back your edit and see if that works. If I'm feeling really productive, I might dig through the Ruby API and actually learn something. I appreciate the help, and thank you for your patience! – Rainbolt – 2014-07-15T14:10:06.240

@Rusher No problem! The 1.9.3/2.0.0 problem was that in 2.0.0 String#chars returns an array, while in 1.9.3 it only returns an enumerable, and sample is defined on array, so the fault was really mine. The API is worth a read, at least for the classes Enumerable, Array and String (I would say these cover ~95% of my Ruby golfing needs). I also tested out the methods online, it should definitely work now. – daniero – 2014-07-15T14:20:17.837

3

Velociraptor, python

Look at the votes in last 3 rounds and make a prediction based on 'velocity'

import sys

def Goodness(round):
    angels = round.count('1')
    return angels / len(round)

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

rounds = sys.argv[1].split(',')
if len(rounds) < 3:
    print('evil')

#get last 3 results and convert to goodness values
y = []
for round in rounds[-3:]:
    y.append(Goodness(round))

velocity_old = y[1]-y[0]
velocity_new = y[2]-y[1]
accel = velocity_new - velocity_old
prediction = Goodness(rounds[-1]) + velocity_new + accel

if prediction > 0.5:
    print('evil')
else:
    print('good')

Name Velociraptor.py, execute python3 Velociraptor.py

tzazy

Posted 2014-07-08T20:18:06.290

Reputation: 61

Clever Girl.... – Stranjyr – 2014-07-29T19:16:28.730

3

Cyclist

Tries to find emerging cycles by looking for repetition in recent past.

import sys
from collections import Counter

def common_prefix(a, b):
    return ([x[0] == x[1] for x in zip(a, b)] + [False]).index(False)

if len(sys.argv) <= 1:
    print("evil")
else:
    rounds = sys.argv[1].split(',')
    winners = tuple(Counter(round).most_common(1)[0][0] for round in rounds)[::-1]
    best_match = 0
    best_i = 0
    for i in range(1, min(20, len(rounds) // 2)):
        match = common_prefix(winners[:i], winners[i:2*i])
        if match * best_i >= best_match * i:
            best_i = i
            best_match = match
    prediction = int(winners[best_i])
    print(["good", "evil"][prediction])

zch

Posted 2014-07-08T20:18:06.290

Reputation: 131

It looks like this is written in Python 3, but you tried to do integer division using a single slash, which won't work. I edited it for you. – Rainbolt – 2014-07-19T07:06:38.117

@Rainbolt I don't know why it looks that way to you - I used / as integer division. Sorry for not specifying, I thought python2 is default Python. But if it now works in 3 then this should be okay as well. – zch – 2014-07-19T09:32:45.073

3

public class AnakinSkywalker extends Human {

    @Override
    public String takeSides(String history) throws Exception {
        String[] events = history.split(",");

        // always start with faith in the light side
        if (events.length == 0)
            return "good";

        // calculate the number of times each player was good.
        int[] goodness = new int[events[0].length()];
        for (String event : events) {
            for (int i = 0; i < event.length(); i++) {
                if (event.charAt(i) == '0') {
                    goodness[i]++;
                }
            }
        }

        // The number of people who are good more than half the time.
        int jedi = 0;
        for (int i = 0; i < goodness.length; i++) {
            if (goodness[i] > events.length / 2)
                jedi++;
        }

        // Side with jedis if they're a majority.
        if (jedi > events[0].length() / 2)
            return "good";

        // Otherwise, fall to the dark side.
        return "evil";
    }
}

Axoren

Posted 2014-07-08T20:18:06.290

Reputation: 125

I included your submission almost as soon as you added it, so if you make any changes today, please ping me with @Rusher. If it's later than tomorrow, don't worry about it (I'll check for recent changes). – Rainbolt – 2014-07-19T08:44:20.917

@Rainbolt, thanks. I didn't test it, though. I wrote it from scratch in the textbox, so forgive me if it does not even compile. – Axoren – 2014-07-19T08:45:30.717

1You were off by one letter lol. chatat(). I fixed it. – Rainbolt – 2014-07-19T08:52:39.683

1Oh such dishonor... I must commit Sudoku. Thanks, lol. – Axoren – 2014-07-19T08:53:51.963

Kay. GL with your sudoku. – Rainbolt – 2014-07-19T09:05:44.457

2

Change Is Constant

Tries to predict the next round based on players changing their minds in the last 3 rounds. Uses BigInteger, just in case we have more than 32 players.

package Humans;

import java.math.BigInteger;

public class ChangeIsConstant extends Human {
    public String takeSides(String history) {
        if(history.length() == 0) {
            return "good";
        }
        String[] hist = history.split(",");
        int players = hist[0].length();
        BigInteger val = BigInteger.ZERO;
        for(int i = Math.max(0, hist.length - 3); i < hist.length; i++) {
            val = val.xor(new BigInteger(hist[i], 2));
        }
        int good = val.bitCount();
        return (good > players - good) ? "good" : "evil";
    }
}

Brilliand

Posted 2014-07-08T20:18:06.290

Reputation: 1 166

After all these years, I look back at this and realize that I got "good" and "evil" swapped on the last line. This submission was playing to lose (trying to vote with the majority). – Brilliand – 2019-09-12T08:01:35.803

2

Death

Death doesn't care about sides, he cares about the people involved.

package Humans;

import java.util.Random;

public class Death extends Human {
    private static int currRound = 0;

    @Override
    public String takeSides(String history) throws Exception {
        if (currRound++ < 2) { //not enough data available
            return Math.random() < 0.5 ? "good" : "evil";       
        }

        String[] rounds = history.split(",");
        int playerCount = rounds[0].length();
        int[] nextTurns = new int[playerCount];

        for (int i = 0; i < playerCount; i++) {
            char[] sides = new char[rounds.length];
            for (int j = 0; j < rounds.length; j++) {
                sides[j] = rounds[j].charAt(i);
            }
            nextTurns[i] = getNextTurn(sides);
        }

        return getMajority(nextTurns);
    }

    private int getNextTurn(char[] rounds) {
        int goodCount = 0;
        for (char round : rounds) {
            if (round == '1') {
                goodCount++;
            }
        }
        double percent = (double) goodCount/rounds.length*100;

        //pretty sure they will return same value
        if (percent > 80) {
            return 1;
        } else if (percent < 20) {
            return 0;
        }

        //calculate random percent
        Random rand = new Random();
        int luck = rand.nextInt(100);
        if (luck < percent) {
            return 1;
        } else {
            return 0;
        }
    }

    private String getMajority(int[] predictedSides) {
        int good = 0;
        int evil = 0;
        for(int vote : predictedSides) { //copied from scoreboard
            if (vote == 0)
                evil++;
            else
                good++;
        }
        return good > evil ? "good" : "evil";
    }

}

Tries to predict the next side for each player. Due to the weak "algorithm", it doesn't perform very well...

CommonGuy

Posted 2014-07-08T20:18:06.290

Reputation: 4 684

1I wish I could give you two upvotes for the clever algorithm and also for being the first of nearly forty Java submissions to not have a single warning in your code when I copy it to NetBeans. You even added the @Override annotation lol. – Rainbolt – 2014-07-15T02:37:56.043

2

Java, Friendship

This one makes friends based on other players who voted like it did at the time. Tends to follow the Angel, but can turn "evil" if convinced.

public class Friendship extends Human {
    private int[] friends = null;
    private char myLastVote = '1';

    private int makeFriends(String lastRound) {
        if (friends == null) {
            friends = new int[lastRound.length()];
        }

        //Preset my next vote against my last vote, to cancel out that I'm my own best friend
        int nextVote = (myLastVote == '0' ? 1 : -1);

        //Update my friend list
        for (int i = 0; i < friends.length; i++) {
            char friendVote = lastRound.charAt(i);
            friends[i] += (friendVote == myLastVote ? 1 : -1);
            if (friends[i] > 0 ) {
                nextVote += (friendVote == '0' ? -1 : 1);
            }
        }

        //Set my vote as the same as my friend's last vote
        if (nextVote > 0) {
            myLastVote = '1';
        } else if (nextVote < 0) {
            myLastVote = '0';
        }
        return nextVote;
    }

    @Override
    public String takeSides(String history) throws Exception {
        if (history.length() > 0) {
            String[] rounds = history.split(",");
            //Create friends array from history, if non-existent
            if (friends == null) {
                for (String round : rounds) {
                    makeFriends(round);
                }
            } else {
                makeFriends(rounds[rounds.length-1]);
            }

        }
        return myLastVote == '0' ? "evil" : "good";
    }

}

Wasmoo

Posted 2014-07-08T20:18:06.290

Reputation: 21

1How do you know your last vote? You don't persist anything and there will be a new instance every round. Also you may add code highlighting. – Angelo Fuchs – 2014-07-14T08:04:50.700

When friends is null (i.e. nothing persisted), Friendship recreates its friends and its last vote by replaying all of the previous rounds. The algorithm is deterministic, so it will always define the same friends. Similarly, it will always find itself as its best friend. – Wasmoo – 2014-08-06T18:06:02.013

2

Parity Check, Ruby

Becomes evil if the number of ones is uneven.

puts %w(good evil)[$*[0] && $*[0].count("1") % 2 || 0]

Run with ruby parity.rb

daniero

Posted 2014-07-08T20:18:06.290

Reputation: 17 193

2

PokerFace

(not a valid entry)

This guy tries to make the results more predictable by manipulating the result of Math.random(), then takes a side based on what one of a few other Humans would do. If those classes are missing or time becomes an issue, he's evil.

package Humans;

import java.lang.reflect.Field;
import java.util.Random;

public class PokerFace extends Human {

    private static Random pokerFace;
    private static Field f;
    private static String copyCat[] = {
        "BackPacker",
        "Clairvoyant",
        "PressuredPeer",
    };

    static {
        pokerFace = new Random() {
            @Override
            public double nextDouble() {
                return 1.0; // gamblers don't gamble
            }
        };
        try {
            f = Math.class.getDeclaredField("randomNumberGenerator");
            f.setAccessible(true);
            applyPokerFace();
        } catch (Exception e) {
        }
    }

    private static void applyPokerFace() {
        try {
            if(f.get(null) != pokerFace)
                f.set(null, pokerFace);
        } catch (Exception e) {
        }
    }

    @Override
    public String takeSides(String history) throws Exception {
        long start = System.currentTimeMillis();
        applyPokerFace();
        for(String s : copyCat) {
            if(System.currentTimeMillis() > start + 950) break;
            try {
                if(s.equals("PokerFace")) continue;
                return ((Human) Class.forName("Humans." + s).newInstance()).takeSides(history);
            } catch (Throwable e) {}
        }
        return "evil";
    }

}

mackthehobbit

Posted 2014-07-08T20:18:06.290

Reputation: 314

2I was prepared for this type of thing when I wrote the challenge. You may not modify the structure or behavior of anything except your own class. You modified the behavior of the Math class, and so I have to disqualify the submission. – Rainbolt – 2014-07-12T04:22:10.200

2

Mr NotSure

Mr NotSure may change his side in each round depends on other players votes. In general he tries to predict from past how often players change their decisions and adopt it in next round. In example if players change (0 in 1 round and 1 in 2 round) their sides in previous rounds more often than hold (0 in 1 round and 0 is 2 round) their picks he will vote in next round on majority from previous round. That's because probably people will change their mind again (so he will vote on minority).

package Humans;

import java.util.ArrayList;
import java.util.List;


public class MrNotSure extends Human {
    private final List<List<Boolean>> rounds = new ArrayList<>();

    private int ALL_ROUNDS_SCORE = 0; 

    @Override
    public String takeSides(String history) throws Exception {
        if(history == null || history.isEmpty())
            return "evil";
        String[] roundsString = history.split(",");
        if(roundsString.length <= 4)
            return "evil";

        readDataToList(roundsString);

        List<Integer> roundChangeScore = computeChangesForEachRound();

        Round lastRound = new Round(roundsString[roundsString.length-1]);
        if(lastRound.getDifference() < 2)
            return lastRound.getMajority();

        if(ALL_ROUNDS_SCORE < 0)
            return lastRound.getMajority();
        else
            return lastRound.getMinority();
    }

    private List<Integer> computeChangesForEachRound() {
        List<Integer> roundChangeScore = new ArrayList<>();
        int outOfBoundsIndex = rounds.size();
        for(List<Boolean> round : rounds)
        {
            int currentIndex = rounds.indexOf(round);
            if(currentIndex + 1 < outOfBoundsIndex)
            {
                Integer score = computeChanges(round, rounds.get(currentIndex + 1));
                ALL_ROUNDS_SCORE += score;
                roundChangeScore.add(score);
            }
        }
        return roundChangeScore;
    }

    private Integer computeChanges(List<Boolean> roundA, List<Boolean> roundB) {
        Integer b = 0;
        for(int i=0; i<roundA.size(); i++)
        {
            if(roundA.get(i).equals(roundB.get(i)))
                b++;
            else
                b--;
        }
        return b;
    }

    private void readDataToList(String[] roundsString) {
        for(String s : roundsString)
        {
            List<Boolean> round = new ArrayList<>();
            for(char a : s.toCharArray())
            {
                if(a == '0')
                    round.add(false);
                else
                    round.add(true);
            }
            rounds.add(round);
        }
    }

    private class Round
    {
        int good = 0;
        int evil = 0;
        int difference = 0;
        public Round(String round)
        {
            for(char a : round.toCharArray())
            {
                if(a == '0')
                    evil++;
                else
                    good++;
            }
            difference = Math.abs(good - evil);
        }

        public String getMinority() {
            if(good > evil)
                return "evil";
            else
                return "good";
        }

        public String getMajority() {
            if(good > evil)
                return "good";
            else
                return "evil";
        }

        public int getDifference()
        {
            return difference;
        }
    }
}

kukis

Posted 2014-07-08T20:18:06.290

Reputation: 121

2

Haskell: Copy Cat

Note: This is Literate Haskell. This means this post is the program itself, and this is a comment in that program.

My program just finds out who's winning, and then copies their last move.

First, the modules.

> import Control.Applicative
> import Data.Function
> import Data.List
> import Data.List.Split
> import System.Environment

Now I invent a function that automatically passes the arguments of a program to a pure function, and then outputs its result to stdout.

> main = argInter prog
> argInter :: ([String] -> String) -> IO ()
> argInter s = getArgs <**> pure s >>= putStrLn

Now for a type for Good and Evil.

> data Moral = Evil | Good deriving (Eq, Read)
> instance Show Moral where
>   show Evil = "evil" --We need lower case.
>   show Good = "good"

We now have a function for converting characters to Morality.

> toMorality :: Char -> Moral
> toMorality '0' = Evil
> toMorality '1' = Good
> toMorality a   = error $ "'"++a:"' is not a vaild Morality."

We show the result of copycat as a String. For the arguments to copycat, first we split on ",", and then we convert the characters to Morality.

> prog [] = show Good -- good seems like a good choice for no arguments
> prog [arg] = show $ copycat $ map (map toMorality) $ splitWhen (==',') arg
> prog a      = error $ "Only 0 or 1 arguments excepted, recieved " ++ (show $ length a) ++ " arguments!"

And now for the meat. We give to copymax the points of all other players. We fold a function that adds points to players scores (and keeps track of how many times the where in the minority.) We then just get the scores with map fst.

> copycat :: [[Moral]] -> Moral
> copycat info = copymax . map fst . foldl' scorer (repeat (0,0)) $ info
>   where

scorer just zips together the choice of each player this round with each players points using the incr function, we updates each players points based on their choice.

>       scorer points round = zipWith incr round points
>           where

To find the majority, we partition the set into the Good and the not Good, compare the lengths. incr is round for each player each round.

>               majority=let (g, e) = partition (==Good) round in case length g `compare` length e of
>                   GT -> Good
>                   LT -> Evil
>                   _  -> error "No majority!"
>               incr morality (points, minor) = if morality==majority
>                   then (points+3, 0)
>                   else (points+minor, minor+1) --minor+1-1 == minor

copymax then just zips the points with the last round, chooses the maximum pair based on the points, and then takes the choice of the last round.

>       copymax points = fst $ maximumBy (compare `on` snd) $ zip (last info) points


Copy this entire post into a text editor and save as copycat.lhs (mind the l).

To compile, first install the haskell platform. Then do cabal install split. Then do ghc -O2 copycat.lhs.

To run: do copycat.exe args if you're on windows.

PyRulez

Posted 2014-07-08T20:18:06.290

Reputation: 6 547

Ignore my last comment. I removed all of the comments in your code and it finally started to compile but then I got Not in scope: 'info'. What should I do? – Rainbolt – 2014-07-14T01:28:50.323

@Rusher info is indeed out of scope there. I think PyRulez didn't compile his code before posting. If I copy this whole post into a text editor and change the line starting with > copycat = to read > copycat info = copymax . map fst . foldl' scorer (repeat (0,0)) $ info and save it as copycat.lhs (mind the l) and run ghc -O2 copycat.lhs, it compiles nicely and it works. – tomsmeding – 2014-07-14T15:45:15.170

@tomsmeding Can you edit the post to reflect what you think is an improvement? I am not at all familiar with Haskell and would feel better if someone who was made the change. – Rainbolt – 2014-07-14T15:54:25.687

1@Rusher I submitted an edit to reflect what I just said. EDIT: I see it got accepted. – tomsmeding – 2014-07-14T16:04:29.980

The submission is running fine, but I had to add in some blank lines to separate the comments from the code. Stack Overflow renders blank lines, but they don't show up in Notepad sometimes. I don't even know if this can be fixed in a way that I can just copy and paste the text, so I wouldn't worry about it. – Rainbolt – 2014-07-15T01:08:09.850

I thought it did compile before, huh? (Sorry, gone for a week.) – PyRulez – 2014-07-19T21:08:22.963

@Rainbolt Apparently my entry caused an "exception." What was this exception? My program should give meaningful errors. – PyRulez – 2014-07-24T22:50:38.367

2

The Friend of the Lonely

This character finds the first lonely person (defined as being surrounded by 2 of the opposite values, e.g. 00100 has a 1 as lonely; if there are less than 5 players we'll simply use 1 boundary: 010), and chooses that player's side.

As per @trlkly's request, this is written in Fortran 90 -- I apologize in advance for its length, but getting an okay PRN is a bit complicated in Fortran. Anyway, here's the code:

program FriendOfLonely
   implicit none
   integer, parameter :: ip = selected_int_kind(16)
   real :: r
   integer(4) :: first, i, j, competitors
   character(len=999999) :: round
   character(len=4) :: choice


   call init_random_seed()

   first = command_argument_count()
   if(first == 0) then
      call random_number(r)
      choice = merge("good", "evil", r > 0.5)
   else
      call get_command_argument(first, round)
      j=len(trim(round))
      do i=1,j
         if(round(i:i)==',') then
            competitors=i-1
            exit
         endif
      enddo
      call Choose(competitors, first, trim(round(j-competitors+1:j)), choice)
   endif

! I've made my choice
   print *,choice
 contains
   !> Search the array for choice
   subroutine Choose(comps, first, round, choice)
     integer(4), intent(in) :: comps, first
     character(*), intent(in) :: round
     character(4), intent(out) :: choice
     character(5) :: good, evil
     integer(ip) :: i,j

     if(comps >= 5) then
        good = '00100'; evil = '11011'
! find a lonely person
        do i=1,len(round)-5
           if(round(i:i+4)==good) then
              choice = 'good'
              return
           else if(round(i:i+4)==evil) then
              choice = 'evil'
              return
           endif
        enddo
     else
        good = '010'; evil = '101'
! find a lonely person
        do i=1,len(round)-3
           if(round(i:i+2)==trim(good)) then
              choice = 'good'
              return
           else if(round(i:i+2)==trim(evil)) then
              choice = 'evil'
              return
           endif
        enddo
     endif
! make random choice
     call random_number(r)
     choice = merge('good', 'evil', r > 0.5)
   end subroutine Choose

   !> Sets the PRNG so you're not constantly choosing the same damn thing
   subroutine init_random_seed()
      integer(4) :: n, i, un, istat, dt(8), pid
      integer(ip) :: t
      integer(4), allocatable :: seed(:)
      call random_seed(size=n)
      allocate(seed(n))
      call system_clock(t)
      if(t==0) then
         call date_and_time(values=dt)
         t = (dt(1) - 1970) * 365 * 24 * 60 * 60 * 1000 &
                       + dt(2) * 31 * 24 * 60 * 60 * 1000 &
                       + dt(3) * 24 * 60 * 60 * 1000 &
                       + dt(5) * 60 * 60 * 1000 &
                       + dt(6) * 60 * 1000 + dt(7) * 1000 &
                       + dt(ip)
      endif
      pid = getpid()
      t = ieor(t, int(pid,kind(t)))
      do i=1,n
         seed(i) = lcg(t)
      enddo
      call random_seed(put=seed)
   end subroutine init_random_seed

   !> Not ideal PRNG, but it will work
   integer(ip) function lcg(s)
     integer(ip) :: s
     if(s==0) then
        s = 104729
     else
        s = mod(s, 4294967296)
     endif
     s = mod(s* 279470273, 4294967291)
     lcg = int(mod(s, int(huge(0), 8)), 8)
   end function lcg
end program FriendOfLonely

This is compiled via

gfortran -fno-range-check -o FriendOfLonely FriendOfLonely.f90

and can accept the command line arguments (not something normally done in Fortran, but the language can handle just about anything these days).

Kyle Kanos

Posted 2014-07-08T20:18:06.290

Reputation: 4 270

@trlkly: as per your request ;) – Kyle Kanos – 2014-07-14T13:55:08.230

@Rusher: Ooo, Windows. I haven't used Windows in 5+ years. Intel offers a trial version for Windows (ifort -o FriendOfLonely FriendOfLonely.f90 would compile it), but I've never used it (the Linux version I have).

– Kyle Kanos – 2014-07-15T01:39:42.877

1I managed to install mingw-get. I flopped around in the help section until I was able to install gfortran. Then I installed mpc. I am at least now getting what look like errors when I compile Error: invalid instruction suffi x for pop and Error: register save offset not a multiple of 8. Can someone add a more complete set of instructions for compiling this on a Windows machine? I've downloaded three packages already trying to make this work, and the error messages looks like weird assembly language errors. – Rainbolt – 2014-07-15T01:54:43.390

2

Probability Gambler

Determines each player's probability towards good and votes for the minority.

public final class ProbabilityGambler extends Human {

    @Override
    public String takeSides(String history) throws Exception {
        if (history.length() == 0) return "evil";

        String[] rounds = history.split(",");
        double players[] = new double[rounds[0].length()];
        for (String round : rounds) {
            for (int i = 0; i < players.length; i++) {
                if (round.charAt(i) == '1') {
                    players[i] = players[i] + 1;
                }
            }
        }

        double expected = 0;
        for (int i = 0; i < players.length; i++) {
            expected += players[i]/rounds.length;
        }

        return 1-(int)Math.round(expected / players.length) == 1 ? "good" : "evil";
    }
}

Wasmoo

Posted 2014-07-08T20:18:06.290

Reputation: 634

2

The Historian

The Historian carefully makes his way through history, accumulating one previously casted vote to his total every round. If the total is even he sides with evil, otherwise he joins the forces of good.

 package Humans;

 public class Historian extends Human{

 public final String takeSides(String history) { 
     if (history.length()==0){return "evil";}
     String cumulative_history=history.replace(",", "");
     int round_number=history.length() - cumulative_history.length();
     int accumulation=0;
     for (int i=0; i<round_number; i++){
         if (history.charAt(i)=='1')accumulation++;
     }
     if (accumulation%2==0){
         return "evil";
     }
     else {
         return "good";
     }
 }
 }

The Skeptic

The Skeptic works opposite to the way the historian does as he has trouble believing data from the past. He accumulates a total starting from the most recent results, adding a previous vote for each round he's completed.

 package Humans;

 public class Skeptic extends Human{
 public final String takeSides(String history) { 
     if (history.length()==0){return "good";}
     String cumulative_history=history.replace(",", "");
     int round_number=history.length() - cumulative_history.length();
     int accumulation=0;
     for (int i=cumulative_history.length(); i>cumulative_history.length()-round_number; i--){
         if (history.charAt(i)=='1')accumulation++;
     }
     if (accumulation%2==0){
         return "good";
     }
     else {
         return "evil";
     }
 }
 }

Nexus

Posted 2014-07-08T20:18:06.290

Reputation: 641

2

Average Loser

if ARGV.length == 0
    puts "evil"
else
    previous_rounds = ARGV[0].split(',')
    evils = 0
    goods = 0

    previous_rounds.each do |round|
        round.to_s.chars.each do |vote|
            vote == "0" ? evils += 1 : goods += 1
        end
    end

    puts (evils < goods) ? "evil" : "good"
end

To run:

ruby average_loser.rb

Average looser looks at the entire history and selects the one which has the average lowest total vote count. The hope is that other approaches are so over-complicated that they tend towards one direction or the other, thus giving this one an advantage.

Or it will simply mostly lose because it is not smart enough, and not dumb enough.

Automatico

Posted 2014-07-08T20:18:06.290

Reputation: 171

2

The Laughing Man

package Humans; 

/* 
And remember, while you're out there risking your life and limb through 
shot and shell, we'll be in here thinking what a sucker you are. 
~Groucho Marx
*/

public class LaughingMan extends Human {

    @Override
    public String takeSides(String history) throws Exception {
        if(history.length() == 0)
            return "good";
        if ((history.split(",").length % 2) == 1)
            return "evil";
        else
            return "good";
    }

// lolololololololololo

}

Pretty self-explanatory.

Zibbobz

Posted 2014-07-08T20:18:06.290

Reputation: 131

@James_pic I thought I might ping you in the event that you want to retract your downvote now that the answer does compile. I laughed pretty hard when I finally understood the joke. – Rainbolt – 2014-07-16T16:28:37.097

@Rusher Dang, forgot to edit it a bit to suit the contest, and the return type no less! Thank you Rusher. :) – Zibbobz – 2014-07-16T16:29:50.190

@m.buettner It works now I think. Zibbobz, I changed your logic a bit to get the effect I think you were after. – Rainbolt – 2014-07-16T16:50:19.227

@Rusher This is what I get for trying to rush this thing out. Thank you for the help. Seriously. – Zibbobz – 2014-07-16T17:02:29.097

Downvote revoked. It looks good now. – James_pic – 2014-07-16T17:30:48.093

2

Schizophrenic

This guy has scizophrenia. He has an angel, a demon, and a monkey whispering things into his ear, and he decides which voice he's going to listen to.

package Humans;


public class Schizophrenic extends Human {

 ShoulderFriend monkey = new ShoulderMonkey(), angel = new ShoulderAngel(), demon = new ShoulderDemon();

 // Sample inputs: "101,111,000,110"
 public String takeSides(String history)
 {
     String[] ages = history.split(",");
     return pickVoice(ages).listen();

 }

 public ShoulderFriend pickVoice(String[] ages)
 {
     int m = monkey.score(ages), a = angel.score(ages), d = demon.score(ages);
     if (m >= a && m >= d)
        return monkey;
     if (a >= d)
        return angel;
     return demon;
 }

 abstract class ShoulderFriend
 {
    abstract String listen();
    abstract int score(String[] ages);
    boolean didWin(int choice, String age)
    {
        String choicestr = choice == 0 ? "0" : "1";
        // Counts by taking checking length of string if choice was removed
        int count = age.length() - age.replace(choicestr, "").length();
        return count > age.length() / 2;
    }

 }

 class ShoulderMonkey extends ShoulderFriend
 {
     int[] decisions = new int[1000];
     String listen()
     {
         return Math.random() < 0.5 ? "good" : "evil";
     }
     int score(String[] ages)
     {
         int score = 0;
         for (int i = 0; i < ages.length; i++)
            if (didWin(decisions[i], ages[i]))
                score += 3;
         return score;
     }

 }
 class ShoulderAngel extends ShoulderFriend
 {
     String listen()
     {
         return "good";
     }
     int score(String[] ages)
     {
         int score = 0;
         for (String s : ages)
            if (didWin(0, s))
                score += 3;
         return score;
     }

 }
 class ShoulderDemon extends ShoulderFriend
 {
     String listen()
     {
         return "evil";
     }
     int score(String[] ages)
     {
         int score = 0;
         for (String s : ages)
            if (didWin(1, s))
                score += 3;
         return score;
     }
 }
}

thefistopher

Posted 2014-07-08T20:18:06.290

Reputation: 151

2

The Unpopular Voter

Here is my solution,

the theory is thus,

find the people who vote consistantly over the last x votes pick the least popular consistant vote

unpop.py:

import sys
import random


def getVote(input):
    rounds = input.split(",")
    players = len(rounds[0])
    lastvote = rounds[len(rounds)-1]

    v0 = 0
    v1 = 0
    for p in xrange(players):
        i = 2
        votes = 1
        lv = rounds[len(rounds)-1][p]
        while i<=len(rounds) and i<40 and rounds[len(rounds)-i][p] == lv:
            votes+=1
            i+=1
        #print lv,votes
        if votes>20:
            if lv == '0':
                v0+=1
            else:
                v1+=1
    #print "last votes 0:",v0,"1:",v1
    if v0>v1:
        return "good"
    else:
        return "evil"

if len(sys.argv)==1 or len(sys.argv[1])<1:
    if random.random()>0.5:
        print "good"
    else:
        print "evil"
else:
    input = sys.argv[1]
    print getVote(input)

James Peach

Posted 2014-07-08T20:18:06.290

Reputation: 21

2

JAVA: Opportunist


package Humans;

/**
 * Always follows the group who was strongest the last turn.
 */
public class Opportunist extends Human{

    @Override
    public String takeSides(String history) throws Exception {
        int participants = history.length();
        String str = history.replace("0", "");
        int good = str.length();
        return (good > participants/2 ? "good" : "evil");
    }


}

Andreas

Posted 2014-07-08T20:18:06.290

Reputation: 147

2

Python: Champion

When everyone switches to the loosing side, it's obviously better to stick with the winner.

Computes an exponentially weighted moving average of the mean values through the history to determine the long term winning side.

import sys
import random

def choose(x):
    # choose winning side
    print "good" if x > 0.5 else "evil"

if len(sys.argv) == 1:
    choose(random.random())
else:
    history = sys.argv[1].split(',')
    # compute exponentially weighted average through history
    ewma = 0.5
    alpha = 0.15
    for x in history:
        mean = sum(float(i) for i in x)/len(x)
        ewma += alpha*(mean - ewma)
    choose(ewma)

moooeeeep

Posted 2014-07-08T20:18:06.290

Reputation: 171

2

Grape (Python)

Mr. Miyagi says

Walk on road, hm?
Walk left side, safe.
Walk right side, safe.
Walk middle, sooner or later, get squish just like grape.

This program chooses the "middle of the road:" the center vote in the list of all votes. When this is not a viable answer, it chooses "good."

import sys

if len(sys.argv) == 1:
    print('good')
else:
    history = sys.argv[1]
    if history[len(history) // 2] == '0':
        print('evil')
    else:
        print('good')

Run as

python grape.py [args]

Michael - Where's Clay Shirky

Posted 2014-07-08T20:18:06.290

Reputation: 187

2

Gustav, Python 2

This program follows another player's last choice, selecting the player based on how well following them would have performed over the last rounds. For a wider range to choose from, I also include some additional basic strategies. For every possible move, doing exactly the opposite is also considered.

import sys

if len(sys.argv)<2:
    print('evil')

else:
    h = [[int(x) for x in l] for l in sys.argv[1].split(',')]     # history as list of lists
    M, N = len(h), len(h[0])                                      # number of rounds and players
    winners = [sum(l)>N/2 for l in h]                             # list of each round's winning side

    for i in xrange(M):                                           # loop over all rounds
        h[i] += [winners[max(i-s,0)] for s in xrange(min(M,42))]  # add virtual players that voted for the majority of the last rounds
        h[i] += [0]                                               # add a deamon
        h[i] += [i%2]                                             # add a turncoat
        h[i] += map(lambda x: not x, h[i])                        # add opposite move for each player

    N = len(h[0])                                                 # update number of players
    scores, run = [0]*N, [0]*N                                    # initialize player scores

    for i in xrange(M-1):                                         # loop over all rounds but last
        for j in xrange(N):                                       # loop over all players
            in_maj = h[i][j]==winners[i+1]                        # following this player, on which side would I have been next round?
            scores[j] += 3 if in_maj else run[j]                  # score according to the rules
            run[j] = run[j]+1 if not in_maj else 0                # keep track of consecutive minority votes

    print 'good' if h[-1][scores.index(max(scores))] else 'evil'  # follow the player that would have given the highest score so far
                                                                                                     #(sorry about the bad formatting)

Run as:

python Gustav.py

Emil

Posted 2014-07-08T20:18:06.290

Reputation: 1 438

Comments would be nice, apart from that a nice program – german_guy – 2014-07-24T07:34:52.990

@german_guy: Thanks, I'll add a few comments soon. – Emil – 2014-07-24T14:40:11.617

1

theAnswerOfLifeIs42 -Python 2

This program will play randomly on first 100 rounds, but then will aim for majority in the following rounds( by finding pattern in the last 4 moves ), with the exception that every multiple of 42 round, it will hunt for minority instead.

import sys
from random import randint

if len(sys.argv)==1:
    c=[]
else:
    c=sys.argv[1].split(",")

d=len(c)

def iWantRandomNOW():
    what=["evil","good"]
    return what[randint(0,1)]

if len(c)<100 :
    sys.stdout.write(iWantRandomNOW())
else:
    tendency=[]
    for x in xrange(d):
        so=[0,0]
        for y in xrange(d-4):
            if (c[y])[x]==(c[y])[d-4] and (c[y])[y+1]==(c[y])[d-3] and (c[y])[y+2]==(c[y])[d-2] and (c[y])[y+3]==(c[y])[d-3]:
                so[int(c[y+4])]+=1
        if so[0]>so[1]:
            tendency.append(0)
        elif so[1]>so[0]:
            tendency.append(1)
        else:
            tendence.append(randint(0,1))
    if d%42==0: #THE ANSWER OF LIFE IS 42!
        if tendency.count(0)>tendency.count(1):
            sys.stdout.write("good")
        else:
            sys.stdout.write("evil")
    else:
        if tendency.count(0)>tendency.count(1):
            sys.stdout.write("evil")
        else:
            sys.stdout.write("good")

#

Pretty much handled like other python program, can accept data from command line with the commandline theansweroflifeis42.py 101,101,101

Realdeo

Posted 2014-07-08T20:18:06.290

Reputation: 421

1I think you meant "theansweroflifeis42.py" as your command line, didn't you? – William Barbosa – 2014-07-11T16:41:43.720

1Eh.... yes... You pass my test =p – Realdeo – 2014-07-11T16:45:16.807

1

The Lover, Python

This one doesn't care about the mass of people, he only cares about one specific player and always takes the same side his significant other chose last turn.

import sys

if len(sys.argv) > 1:
    rounds = sys.argv[1].split(',')
    loved = len(rounds[0]) / 2
    lovedChoice = rounds[-1][loved]
    if lovedChoice == "1":
        print("good")
    else:
        print("evil")
else:
    print("good")

Run with:

python lover.py

API-Beast

Posted 2014-07-08T20:18:06.290

Reputation: 111

1I changed loved = len(round[0]) / 2 to loved = len(round[0]) // 2 so that I could use Python 3. I hope you don't mind, since the result is exactly the same. – Rainbolt – 2014-07-14T02:07:10.053

1

BigMac, python

Uses MACD moving average to predict next winner.

import sys

# Calculate EMA over a given date range
def EMA(todaysGoodness,days,yesterdaysEMA):
    k = 2/(days+1)
    return todaysGoodness*k + yesterdaysEMA*(1-k)

# Percentage that voted 'good' for a round
def Goodness(round):
    angels = round.count('1')
    return angels / len(round)

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

rounds = sys.argv[1].split(',')

# if we have more than 100 rounds, only look at last 100 to save time
rounds = rounds[-100:]

# Calculate the exponential moving average(EMA) for last 10 rounds and last 5 rounds
# Then take EMA of diference between those two
first = Goodness(rounds[0])
ema_long = [first]
ema_short = [first]
ema_signal = [first]

for round in rounds:
    ema_long.append(EMA(Goodness(round), 10, ema_long[-1]))
    ema_short.append(EMA(Goodness(round), 5, ema_short[-1]))
    ema_signal.append(EMA(ema_long[-1]-ema_short[-1], 2, ema_signal[-1]))

# If the signal EMA exceeds short-long, predict that next majority will be good
# otherwise predict evil
if (ema_signal[-1] > ema_long[-1] - ema_short[-1]):
    print('good')
else:
    print('evil')

Save as BigMac.py and run with python3 BigMac.py

tzazy

Posted 2014-07-08T20:18:06.290

Reputation: 61

1

another one, because it was a simple change from the previous one:

NaiveBayesian

Changed to now just model the expected outcome, using data from 3 previous rounds.

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

using Eigen::MatrixXf;
using Eigen::VectorXf;
using Eigen::IOFormat;
typedef Eigen::Matrix<bool, Eigen::Dynamic, 1> VectorXb;

using std::max;
using std::min;

void count(MatrixXf &feats, VectorXb &classes, MatrixXf &out) {
    // we will store only probability for a feature being
    // present given each of our classes.
    // do some smoothing by adding count of 3 to begin with

    out = MatrixXf::Constant(feats.rows() + 1, 2, 3);

    float n_positive = (classes.array() > 0).count();
    float n_negative = classes.size() - n_positive;

    for (size_t i = 0; i < classes.size(); ++i) {
        // probability of feature given positive        
        if (classes(i)) {
            out.col(0).segment(0,feats.rows()) += feats.col(i);
        } else {
            out.col(1).segment(0,feats.rows()) += feats.col(i);
        }
    }

    // add two to n_positive and n_negative as smoothing
    out.array().col(0) /= n_positive + 6.;
    out.array().col(1) /= n_negative + 6.;

    out(feats.rows(), 0) = static_cast<float>(n_positive) / classes.size(); 
    out(feats.rows(), 1) = static_cast<float>(n_negative) / classes.size();
}

int classify(MatrixXf &weights, VectorXf &feats) {
    float p_positive = 1;
    float p_negative = 1;
    for (size_t i = 0; i < feats.size(); ++i) {
        if (feats(i)) {
            p_positive += log(weights(i,0));
            p_negative += log(weights(i,1));
        } else {
            p_positive += log(1 - weights(i,0));
            p_negative += log(1 - weights(i,1));
        }   
    }
    p_positive += log(weights(feats.size(), 0));
    p_negative += log(weights(feats.size(), 1));

    if (p_positive > p_negative)
        return 1;
    return -1;
}

size_t predict(MatrixXf &train_data, VectorXb &labels, VectorXf &testitem) {
    MatrixXf weights;
    count(train_data, labels, weights);
    return classify(weights, testitem) > 0;
}

static const size_t N = 3;
// 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) {
    // get at least 50 rounds of training data per column of features
    // if possible  
    size_t n_data_iters = min(N, max(1ul, prev_iters / (50*N)));
    size_t n_feats = n_data_iters * n_participants;
    // can't train on first n_data_iters labels, they don't have enough features.
    size_t n_samples = prev_iters - n_data_iters;

    MatrixXf traindata(n_feats, n_samples); 

    for (size_t iter = 0; iter < n_samples; ++iter) {
        for (size_t x = 0; x < n_data_iters; ++x) {
            traindata.col(iter).segment(x * n_participants, n_participants) = data.col(iter + x);
        }
    }
    // find majority for each round
    VectorXb majorities = data.colwise().sum().array() > (n_participants / 2);
    VectorXb labels = majorities.segment(n_data_iters, n_samples);
    VectorXf testitem(n_feats);
    for (size_t x = 0; x < n_data_iters; ++x) {
        testitem.segment(x * n_participants, n_participants) = data.col(n_samples + x).transpose();
    }

    return predict(traindata, labels, testitem);
}

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) = 0;
                ++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();

    // not enough data to train model on    
    if (n_prev_iters == 1) {
        cout << "evil" << endl;
        std::exit(0);
    }

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


    if (predict(data, n_prev_iters, n_participants)) {
        cout << "evil" << endl;
    } else {
        cout << "good" << endl;
    }
}

to compile:

Save the source code in a file called naivebayesian.cc, download the Eigen library and unzip the Eigen folder found inside into the same folder as the source file. Compile with g++ -I. -O3 -ffast-math -o naivebayesian naivebayesian.cc. (pick one of the two names for output).

To Run

just call the executable name

dgel

Posted 2014-07-08T20:18:06.290

Reputation: 91

@Rainbolt, no longer requires c++11, please try again – dgel – 2014-07-25T14:15:22.357

1

The Unknown

from sys import argv

i = str(argv).count('1')
k = 0

while i>1 and k<1000000:
    k += 1
    i = i/2 if not i%2 else 3*i+1

print 'good' if k<1000000 else 'evil'

Run with python unknown.py .

user189

Posted 2014-07-08T20:18:06.290

Reputation: 111

Heh, though this is only unknown if the number of 1's somehow exceeds 10^18, thanks to prior experimental evidence. – histocrat – 2014-07-15T15:43:41.827

@histocrat yeah, it seems to run quite fast up to 10^2048…hm, say some instances of that size. – user189 – 2014-07-15T15:45:38.627

Collatz conjecture counting. Nice. :D – cjfaure – 2014-07-15T20:52:14.130

1

The Original

Always chooses the first vote, no matter what. If it is first, or if it is the first round, always chooses evil.

package Humans; 

/* 
“[I]t was with a good end in mind – that of acquiring the knowledge of good and evil – 
 that Eve allowed herself to be carried away and eat the forbidden fruit. But Adam was 
 not moved by this desire for knowledge, but simply by greed: he ate it because he 
 heard Eve say it tasted good.” -  Moderata Fonte
*/

public class theOriginal extends Human {

    @Override
    public String takeSides(String history) throws Exception {
        if(history.length() == 0)
            return "evil";
        else
            {
             if (history.charAt(0) == '1')
                 return "good";
             else
                 return "evil";
            }
    }
    // First post! 
}

Zibbobz

Posted 2014-07-08T20:18:06.290

Reputation: 131

1

Hate the latest thing

This is probably the dumbest possible attempt at always choosing the minority.

It only looks at the previos round and goes to the side that lost it. In the first round or if there was a tie, it chooses evil.

latesthater.py

#!/usr/bin/python3
import sys

lastround = ''
if len(sys.argv) > 1:
    lastround = sys.argv[1].split(',')[-1]

if lastround.count('1') < lastround.count('0'):
    print('good')
else:
    print('evil')

Love the latest thing

This is probably the dumbest possible attempt at always choosing the majority.

It only looks at the previos round and goes to the side that won it. In the first round or if there was a tie, it chooses evil.

latestlover.py

#!/usr/bin/python3
import sys

lastround = ''
if len(sys.argv) > 1:
    lastround = sys.argv[1].split(',')[-1]

if lastround.count('1') > lastround.count('0'):
    print('good')
else:
    print('evil')

Expectations

I expect the latestlover to perform better than the latesthater. That's because I expect the method to work 40-80% of the time. So, long continuous streaks of correct guessing should be rare, and the latesthater will only outperform the latestlover if they are indeed more often wrong than right.

Choosing the minority correctly is only worthwile if you can regularily do it seven or more times in a row: 0+1+2+3+4+5+6 = 21 = 7*3.

When you have both of them in the game, they usually don't change the outcome of rounds. Only in the first round or after a tie, they help evil.

Paul Thomann

Posted 2014-07-08T20:18:06.290

Reputation: 346

Duh! I just realized that there are several pages of answers, so this is redundant I guess. Sorry ;) – Paul Thomann – 2014-07-19T13:59:20.860

1

George Santayana

George Santayana remembers the past and is trying desperately to repeat it. He assigns a scores to each past round to signify how similar that round (and the rounds before it) was to the last round. If the round had the same majority as the last round, it gets a score of the amount of total players plus the amount of players that gave the same answer in both rounds, George then looks at the rounds before these rounds and adds the score for those two to the total score. He does this until he encounters two rounds where the majority was not the same. After having found the round with the highest score, he picks the majority of the round after that.

Since I'm very new to Java and relatively new to programming in general, I took Angelo Neuschitzer's Backpacker class as a template. I didn't bother to change the way he responded to the first round (though I default to good in stead of evil) and the way he stores the history in an array of strings. I also reused his didGoodWin function, though I rewrote that to not require the amount of players as an argument. Other than that, the code and logic is all mine. Thanks Angelo, hope you don't mind.

package Humans;

public class GeorgeSantayana extends Human {
    private String[] roundVotes;

    @Override
    public final String takeSides(String history)  {
        if (history == null || history.equals(""))         //these last 4 lines copied from BackPacker(https://codegolf.stackexchange.com/questions/33137/good-versus-evil/34294#34294) by Angelo NeuSchitzer.
            return "good";

        roundVotes = history.split(",");                   //this line copied from BackPacker(https://codegolf.stackexchange.com/questions/33137/good-versus-evil/34294#34294) by Angelo NeuSchitzer.

        int bestScore = -1;
        boolean pickGood = true;

        for (int round = 0; round < roundVotes.length - 1; round++) {
            int currentScore = calculateSimilarity(round, roundVotes.length - 1);
            if (currentScore > bestScore) {
                bestScore = currentScore;
                pickGood = didGoodWin(roundVotes[round + 1]);
            }
        }
        if (pickGood)
            return "good";
        else
            return "evil";
    }

    private int getSameVotes(int round1, int round2){
        int votes = 0;

        for(int i = 0; i < roundVotes[round1].length() && i < roundVotes[round1].length(); i++){
            if (roundVotes[round1].charAt(i) == roundVotes[round2].charAt(i)) {
                votes++;
            }
        }

        return votes;
    }

    private int calculateSimilarity(int round1, int round2){
        if (round1 < 0 ¦¦ round2 < 0) {
            return 0;
        }

        int players = roundVotes[0].length();
        int score = 0;

        if (didGoodWin(roundVotes[round1]) == didGoodWin(roundVotes[round2])) {
            score += players + getSameVotes(round1, round2);
            score += calculateSimilarity(round1 - 1, round2 - 1);
        }

        return score;
    }

    private boolean didGoodWin(String round) {       //this function in part copied from BackPacker(https://codegolf.stackexchange.com/questions/33137/good-versus-evil/34294#34294) by Angelo NeuSchitzer.
        int good = 0;
        for (char next : round.toCharArray())
            good += next == '1' ? 1 : 0;
        return (good * 2) > round.length();
    }
}

overactor

Posted 2014-07-08T20:18:06.290

Reputation: 3 500

I'm downvoting this until you credit the user whose code you copied, where appropriate. Just a simple, // Copied from <insert link here> by user <insert user name here> would suffice (for each instance that you copied). – Rainbolt – 2014-07-20T07:41:02.597

@Rainbolt Sorry about that, I was on my girlfriend's computer and quickly submitted it so I could further edit the answer on my own computer, didn't expect anyone would see it in that small window. – overactor – 2014-07-20T07:44:15.800

Thanks for fixing it. Not attributing code is a pet peeve of mine. Since you fixed it, my downvote turned into an upvote. – Rainbolt – 2014-07-20T09:07:35.850

@Rainbolt I updated the code for my calculateSimilarity function, as it was it would have thrown errors. – overactor – 2014-07-21T05:54:45.480

1

The Politician

Calculates the number of good and evil siding humans in the last round, as well as the number of good and evil siding party members. Sides with the majority chosen by the party members (even though one must side with the minority to win).

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>

#define E 2.718281828459045235360287471352

// No one sees this
const char *operator|(const std::string &, const std::string &){return "\x65\166\x69\154";}

unsigned get_pop_size(char *a){
    char *b = a;
    while(*b && *++b != ',');
    return b-a;
}

int main(int argc, char *argv[]){
    if(argc == 1){  
        // Good by default
        std::string good = "good";
        std::fputs(good|good, stdout);
        return 0;
    }
    unsigned population_size = get_pop_size(argv[1]);

    unsigned seed = *(unsigned *)"CAKE\0\0\0\0";
    std::srand(seed);

    // how irrational
    unsigned num_party_members = population_size / E;

    // create a list of party members
    std::vector<bool> party_members(population_size, false);
    std::fill(party_members.begin(), party_members.begin()+num_party_members, true);
    std::random_shuffle(party_members.begin(), party_members.end());

    // calculate majority for all players and for party members only 
    int all_good, all_evil, party_good, party_evil;
    all_good = party_good = all_evil = party_evil = 0;
    // got paid to do this..no one saw anything
    /* nothing on this line */                  all_evil = party_evil = 2;

    int len = std::strlen(argv[1]);
    unsigned offset = len - population_size;
    for(int i = 0; i < population_size; ++i){
        if(party_members[i]){
            // party member
            if(argv[1][i+offset] == '0') ++party_evil;
            else ++party_good;
        }
        if(argv[1][i+offset] == '0') ++all_evil;
        else ++all_good;
    }

    if(all_evil >= all_good || all_evil < all_good){ 
        // who cares what the general population thinks
    }
    std::printf("%d %d %d\n", population_size, party_evil, party_good);
    if(party_evil >= party_good){
        fputs("evil", stdout);
    }else{
        // good?! well, the party is always right.
        fputs("good", stdout);
    }

    return 0;
}

Should be compiled with g++ -o Politician.exe Politician.cpp, and run with Politician.exe.

es1024

Posted 2014-07-08T20:18:06.290

Reputation: 8 953

1

The Prayer - Ruby

def method_missing(m, *args, &block)  
    puts "evil"
end

if praise_the_lord(ARGV)
    puts "good"
end

Run:

ruby prayer.rb

Automatico

Posted 2014-07-08T20:18:06.290

Reputation: 171

1

A skeleton and multiple entries

These are a few of my creations. They all use 'goodvsevil.py', described further below. They are otherwise all quite dumb.

getrichquick.py

This one always copies the decision that made the most points last round (random in case of a tie). It also contains a hint on how you might use 'goodvsevildebug.py' while developing.

As with any get-rich-quick scheme, this method doesn't generally work.

#!/usr/bin/python3
import sys
from goodvsevil import *
# from goodvsevildebug import *

setSaveFile('./getrichquick.pickled')

def decider(stats):
    points = { False:0, True:0 }
    for i in range(stats.players):
        points[stats.playerLines[i][-1]] += stats.scores[i][-1]
    t = points[True]
    f = points[False]
    if t == f:
        return PetyrBaelish()
    else:
        return f < t

run(sys.argv, decider)
# printStats(randomRounds(7, 70, decider))
# printStats(hostRounds(70, decider, [angel, demon] + 4*[PetyrBaelish,]))

findstreaks.py

This one looks for the player(s) with the longest lasting streak of going with or against majority, and votes for minority based on that player's last vote (average if several players have the same length of streak, random if 50:50 among them).

Its fallacy is the assumption that players are always a) going to continue their streak b) by taking the same side again this turn.

#!/usr/bin/python3
import sys
from goodvsevil import *

identity = lambda x: x
negation = lambda x: not x

def decider(stats):
    count = 0
    minvotes = []
    for i in range(stats.players):
        streak = stats.winningStreaks[i][-1]
        filtr = negation if streak.good else identity # negate if majority streak
        if count < streak.length:
            count = streak.length
            minvotes = [filtr(stats.playerLines[i][-1])]
        elif count == streak.length:
            minvotes.append(filtr(stats.playerLines[i][-1]))
    avg = 0.5
    if minvotes:
        avg = average(minvotes)
    if avg > 0.5:
        return True
    elif avg < 0.5:
        return False
    else:
        return PetyrBaelish()

setSaveFile('./findstreaks.pickled')
run(sys.argv, decider)

trendalizer.py

This one likes to be 65% certain to go with the trend. It averages windows of previous 10, 25, 50, 100 and 200 rounds, then averages these results to determine THE trend.
It prefers the trend from majority decisions over that from all votes. If neither trend is sufficiently clear, it chooses sides randomly.

#!/usr/bin/python3
import sys
from goodvsevil import *

setSaveFile('./trendalizer.pickled')

def decider(stats):
    maj = {}
    tot = {}
    for x in (10, 25, 50, 100, 200):
        maj[x] = average(stats.majorityLine, x)
        tot[x] = []
        for i in range(stats.players):
            tot[x].append(average(stats.playerLines[i], x))
        tot[x] = average(tot[x])
    maj = average(maj.values())
    tot = average(tot.values())
    if 0.15 < abs(maj - 0.5):
        return maj > 0.5
    if 0.15 < abs(tot - 0.5):
        return tot > 0.5
    return PetyrBaelish()

run(sys.argv, decider)

coward.py

This one is normally random, but turns to the forces of good when it feels the presence of evil. Which has nothing to do with what's going on in the world, but is based solely upon the position in which coward finds itself.

According to coward's belief, prime numbers are evil, as are the numbers 6 and, therefore, 9. And 0, obviously. He doesn't believe in new age stuff like even prime numbers or prime numbers having several digits. Any numbers containing an evil number are evil.

#!/usr/bin/python3
import sys
from goodvsevil import *

setSaveFile('./coward.pickled')

evil = [0, 3, 5, 6, 7, 9]

def decider(stats):
    if (None == stats.custom) and (stats.myIndex >= 0):
        if stats.myIndex in evil:
            stats.custom = True
        else:
            indexstr = str(stats.myIndex)
            for bad in evil:
                if str(bad) in indexstr:
                    stats.custom = True
                    return angel()
            stats.custom = False
    if stats.custom:
        return angel()
    return PetyrBaelish()

run(sys.argv, decider)

Skeleton for Python 3

Here's a skeleton for everybody who wishes to build upon. By persisting the Stats, it only ever needs to parse the last round.

It consists of two files:

  • goodvsevil.py: provides infrastructure, saving/loading stats to/from file and adding the latest results each round. Six methods are intended for external use:
    • setSaveFile(path):
      sets the path where the stats are saved. Must be called before run().
    • run(argv, decider):
      parses the latest result, adds it to the stats, calls the decider function, correctly prints the decision to stdout, and saves the stats.
      argv will usually be sys.argv
      decider must be a function that takes a Stats object as argument and returns True for 'good', False for 'evil'.
    • average(list, count=0):
      returns the average of the last count elements in list. If count is falsy, the entire list is used.
    • angel, demon, PetyrBaelish:
      are decider functions with the behavior their name indicates.
  • goodvsevildebug.py: may be useful while developing. It can simulate rounds and print stats. It's not to be used in submissions, because it prints stuff to stdout.
    fileStats(filename) takes the path to your save file and prints the stats it contains.

Edit:

  • non-breaking change to run() in goodvsevil.py:
    run has a new parameter suppress, to support silent operation in goodvsevildebug.py
  • new function getSaveFile() in goodvsevil.py:
    returns the path to the savefile. Needed in goodvsevildebug.py.
  • non-breaking changes to fileStats() and printStats() in goodvsevildebug.py:
    they both have a new parameter onlyMe to enable short listing. fileStats() now also tries to append '.pickled' to the filename if it doesn't otherwise exist.
  • non-breaking change to hostRounds() in goodvsevildebug.py:
    there are two new parameters, opponentnames and myname. They are used when printing. opponentnames also determines the filenames for the opponents saved stats. opponentnames are matched to opponentdeciders by position. deciders without a name get named after the position where they are placed.
  • hostRounds() now announces all players positions (converting them to 1-based), shows progress and timing information every 10%, and displays the final ranking (along with average and max times) when done.
  • hostRounds() doesn't simply call the deciders anymore, but passes them to run(). This means that all deciders have a separate automatically updated save file each, and no work or data is shared between them.

Out of curiosity, I ran 1000 rounds with 29 players (1 Homer-clone with 200ms delay, 1 angel, 1 demon, 1 coward, 1 getrichquick, 1 findstreaks, 1 trendalizer, and 22 PetyrBaelishes, each on top of the goodvsevil.py infrastructure).

Average time per round was 72ms for most players, max time 220ms. Only Homer was slower, 280ms on average and 420ms max. Average time in the last 10% (across all 29) was 151ms per player/round.

That includes loading stats from file, parsing last round, updating stats, making a decision, saving stats back to file, and negligible hostRounds() overhead. It does not include the time to start python and load the script. It also doesn't account for the fact that run() prints the decision to stdout before it saves the stats to help meet timing requirements.

Here is the code I used for the experiment. Feel free to use it as a starting point if you'd like to try out my 'framework'. NOTE: if you don't like to wait half an hour or more, lower the number of rounds and/or opponents.

#!/usr/bin/python3
import sys
from goodvsevil import *

def decider(stats):
    'implement my decision logic here'
    import threading        #
    threading._sleep(0.2)   # please, don't do this. It's just here to prove that time measurement works.
    return True

setSaveFile('./testing.pickled')
run(sys.argv, decider)

#### everything below is to be removed before submitting. ####

rounds = 1000
from goodvsevildebug import *

class DevNull:
    def write(self, *args, **kwargs):
        pass

savefile = getSaveFile() # store the save file name
stdout = sys.stdout
sys.stdout = DevNull()   # silence during import

import coward, streakfinder, getrichquick, trendalizer  # these print stuff and change the save file

sys.stdout = stdout      # restore stdout
setSaveFile(savefile)    # restore save file name

opponents = [angel, demon, coward.decider, streakfinder.decider, getrichquick.decider, trendalizer.decider] + 22* [PetyrBaelish,]
oppnames = ['angel', 'demon', 'coward','streakfinder','getrichquick', 'trendalizer', 'PetyrBaelish1', 'PetyrBaelish2']

stats = hostRounds(rounds, decider, opponents, oppnames, '* Homer Clone *')

### NOTE that the stats object/files do not contain the decisions from the very last round.
###      printStats() and fileStats() will therefore report lower scores than hostRounds() did.
#
#printStats(stats)   # print full run info: majority decisions, player decisions and scores
#
#oppnames.remove('angel')        # we know their decisions - no need to display
#oppnames.remove('demon')
#for x in oppnames:
#    print('\n%s:' % (x,))
#    fileStats(x, onlyMe=True)   # print short info about each opponent in oppnames: position, decisions and score

goodvsevil.py:

#!/usr/bin/python3
import pickle, random

def run(argv, decider, suppress=False):
    'run an invocation. Pass argv = sys.argv, decider = function(Stats) returning boolean.'
    stats = updateStats(argv)
    decision = decider(stats)
    if not suppress:
        print('good' if decision else 'evil')
    if stats.myIndex < 0:
        stats._myDecisions.append(decision)
    with open(savefilename, "wb") as file:
        pickle.dump(stats, file=file)
    return (decision, stats)

def angel(stats=None):
    return True
def demon(stats=None):
    return False
def PetyrBaelish(stats=None):
    return random.randint(0,1)

class Streak:
    def __init__(self, good=False, length=0):
        self.good = good        # meaning depends on context
        self.length = length    # streak length in number of rounds

class Stats:
    def __init__(self):
        self.players = 0        # number of players
        self.majorityLine = []  # list of majority decisions of { True:'good', False:'evil' }
        self.playerLines = []   # list of decisions for each player of { True:'good', False:'evil' }
        self.scores = []        # list of scores for each player (3 for majority, streak.length-1 for minority)
        self.totalScores = []   # total score for each player
        self.decisionStreaks = []   # playerLines as lists of streaks
        self.winningStreaks = []    # list of streaks for each player hitting the same group of { True:majority, False:minority }
        self.majorityLineStreaks = [Streak()]   # majorityLine as a list of streaks
        self._myDecisions = []  # used in the first few rounds until I know who I am
        self.myIndex = -1       # which of the playerLines is Me?
        self.totalGood = 0      # total number of player decisions for 'good'
        self.totalEvil = 0      # total number of player decisions for 'evil'
        self.roundsGood = 0     # total number of rounds won for 'good'
        self.roundsEvil = 0     # total number of rounds won for 'evil'
        self.custom = None      # add some custom state object if you wish

savefilename = ''
def setSaveFile(path):
    global savefilename
    savefilename = path
def getSaveFile():
    return savefilename

def majorityVote(sround):
    return sround.count('1') > sround.count('0')

def isGood(char):
    return char == '1'

def average(listnums, lastcount=0):
    if lastcount:
        listnums = listnums[-lastcount:]
    if listnums:
        return sum(listnums) / len(listnums)
    return 0

def updateStats(argv):
    if not savefilename:
        raise Exception('No save file set. Please use setSaveFile(path) right after "import goodvsevil".')
    decision = 0
    lastround = ''
    stats = None
    firstround = (len(argv) < 2)
    if firstround:
        stats = Stats()
    else:
        with open(savefilename, "rb") as file:
            stats = pickle.load(file)
        maxi = len(stats.playerLines) - 1
        lastround = argv[1].split(',')[-1]
        # record majority:
        majvote = majorityVote(lastround)
        if majvote:
            stats.roundsGood += 1
        else:
            stats.roundsEvil += 1
        if stats.majorityLine and (stats.majorityLine[-1] == majvote):
            stats.majorityLineStreaks[-1].length += 1
        else:
            if stats.majorityLineStreaks[-1].length:
                stats.majorityLineStreaks.append(Streak())
            stats.majorityLineStreaks[-1].length = 1
            stats.majorityLineStreaks[-1].good = majvote
        stats.majorityLine.append(majvote)
        # record player histories:
        for i in range(len(lastround)):
            good = isGood(lastround[i])
            if i > maxi:
                stats.playerLines.append([])
                stats.scores.append([])
                stats.totalScores.append(0)
                stats.decisionStreaks.append([Streak()])
                stats.winningStreaks.append([Streak()])
            guessedright = (majvote == good)
            if stats.playerLines[i]:
                if good != stats.playerLines[i][-1]:
                    stats.decisionStreaks[i].append(Streak())
                if guessedright != stats.winningStreaks[i][-1].good:
                    stats.winningStreaks[i].append(Streak())
            stats.playerLines[i].append(good)
            stats.decisionStreaks[i][-1].length += 1
            stats.winningStreaks[i][-1].length += 1
            stats.decisionStreaks[i][-1].good = good
            stats.winningStreaks[i][-1].good = guessedright
            stats.scores[i].append(3 if guessedright else (stats.winningStreaks[i][-1].length - 1))
            stats.totalScores[i] += stats.scores[i][-1]
            if good:
                stats.totalGood += 1
            else:
                stats.totalEvil += 1
        if stats.myIndex < 0:
            # try to find myself:
            candidates = []
            for i in range(len(stats.playerLines)):
                fits = True
                for k in range(len(stats._myDecisions)):
                    if stats._myDecisions[k] != stats.playerLines[i][k]:
                        fits = False
                        break
                if fits:
                    candidates.append(i)
            if len(candidates) == 1:
                stats.myIndex = candidates[0]
        if not stats.players:
            stats.players = len(stats.playerLines)
    return stats

goodvsevildebug.py:

#!/usr/bin/python3
import os, time
from goodvsevil import *

def randomRounds(players, rounds, decider=PetyrBaelish):
    'host a number of rounds against a number of PetyrBaelishes'
    return hostRounds(rounds, decider, (players-1) * [PetyrBaelish,])

def hostRounds(rounds, decider=PetyrBaelish, opponentdeciders=[], opponentnames=[], myname='* me *'):
    'host a number of rounds against a list of opponents, part of which may have names'
    opponentdeciders = opponentdeciders[:]  # copy lists because we'll alter them
    opponentnames = opponentnames[:]
    increment = rounds / 10  # how many rounds are 10%?
    filename = getSaveFile()
    s = ''
    temps = ''
    stats = None
    deciders = []
    names = []
    players = len(opponentdeciders)
    if (players % 2):
        opponentdeciders.append(PetyrBaelish)
        players += 1
    mypos = random.randint(0,players)
    print('my position: %d of %d' % (mypos+1, players+1))
    print()
    players += 1
    found = players * [False,]
    decisions = players * [False,]
    scores = players * [0,]
    streaks = players * [None,]
    times = players * [None,]
    # randomly arrange players:
    print('Positioning:\n------------')
    for i in range(players):
        if i == mypos:
            deciders.append(decider)
            names.append(myname)
        else:
            x = random.randint(0, len(opponentdeciders)-1)
            randchoice = opponentdeciders[x]
            opponentdeciders.remove(randchoice)
            deciders.append(randchoice)
            if opponentnames and (len(opponentnames) > x):
                name = opponentnames[x]
                names.append(name)
                opponentnames.remove(name)
            else:
                names.append(str(i+1))
        print('%02d: %s' % (i+1, names[i]))
    print()
    print('Run:\n----')
    t0 = time.time()
    for r in range(rounds):
        temps = ''
        tstart = time.time()
        for i in range(players):
            # set a different save file for each player:
            if i == mypos:
                setSaveFile(filename)
            else:
                setSaveFile('./%s.pickled' % (names[i],))
            # call the player, with or without argv[1]:
            if r == 0:
                (decision, st) = run([None], deciders[i], suppress=True)
                times[i] = []
            else:
                (decision, st) = run([None, s], deciders[i], suppress=True)
            # remember everything we need to score the final round:
            decisions[i] = decision
            if r > 0:
                scores[i] = st.totalScores[i]
                streaks[i] = st.winningStreaks[i][-1]
            # prepare next round's argv[1]:
            temps += '1' if decision else '0'
            # check if player has determined its position this round:
            if (not found[i]) and (st.myIndex >= 0):
                found[i] = True
                if i == mypos:
                    print('*** Found myself in position %d after round %d.' % (st.myIndex+1, r))
                else:
                    print(" '%s' found itself in position %d after round %d." % (names[i], st.myIndex+1, r))
            if i == mypos:
                stats = st
            # measure time:
            tend = time.time()
            times[i].append(tend - tstart)
            tstart = tend
        # finish preparation of next argv[1]:
        if r != 0:
            s += ','
        s += temps
        # Inform about progress in steps of ~10%:
        if 1 > ((r+1) % increment): # 1> instead of 0== because increment is float
            print('%d%% - t0 + %f' % (10*(r+1)/increment, time.time()-t0))
    print()
    # score the last round:
    majority = (players/2) < sum(decisions)
    maxtimes = players * [0,]
    for i in range(players):
        if decisions[i] == majority:
            scores[i] += 3
        elif not streaks[i].good:   # minority streak
            scores[i] += streaks[i].length
        maxtimes[i] = max(times[i])
        times[i] = average(times[i])
    scores = list(zip(scores, names, times, maxtimes))
    scores.sort(key = lambda x: x[1]) # sort by name, so that equal scores will appear in lexical ordering
    scores.sort(key = lambda x: x[0], reverse=True) # sort by score
    print('Ranking:\n--------')
    for x in scores:
        print('%d - %s (avg time %f, max %f)' % x)
    print()
    print('hosted %d rounds with %d players in %f seconds.' % (rounds, players, time.time()-t0))
    print()
    return stats

def fileStats(filename, onlyMe = False):
    'load a Stats object from file and print relevant info.'
    if not os.path.exists(filename):
        if os.path.exists(filename + '.pickled'):
            filename += '.pickled'
    with open(filename, 'rb') as file:
        printStats(pickle.load(file), onlyMe)

def printStats(stats, onlyMe=False):
    "print relevant info from the passed Stats object. if onlyMe, don't print majority or opponents."
    playerpaths = ''
    playerguessing = ''
    players = len(stats.playerLines)
    majoritypath = ''
    if onlyMe:
        score = 0
        if stats.myIndex >= 0:  # I found myself at some point, use playerLines[myIndex]
            print('Position: %d / %d' % (stats.myIndex+1, stats.players))
            for i in range(len(stats.playerLines[stats.myIndex])):
                dec = stats.playerLines[stats.myIndex][i]
                majoritypath += '1' if dec else '0'
                playerguessing += '1' if dec == stats.majorityLine[i] else '0'
            score = stats.totalScores[stats.myIndex]
        else:                   # I never found myself, use _myDecisions
            print('Position: unknown')
            good = False
            streak = 0
            for i in range(len(stats._myDecisions)-1):
                dec = stats._myDecisions[i]
                majoritypath += '1' if dec else '0'
                if good == (dec == stats.majorityLine[i]):
                    if good:
                        score += 3
                    else:
                        score += streak
                    streak += 1
                else:   # change between minority/majority
                    good = (dec == stats.majorityLine[i])
                    streak = 1
                    if good:
                        score += 3
                playerguessing += '1' if good else '0'
        print('- %s\n  %s (%d)' % (majoritypath, playerguessing, score))
        return
    for i in range(players):
        decs = '- '
        ress = '- '
        pscore = 0
        for decision in stats.playerLines[i]:
            decs += '1' if decision else '0'
        for streak in stats.winningStreaks[i]:
            ress += streak.length * ('1' if streak.good else '0')
        ress += ' (%d)' % stats.totalScores[i]
        if i == stats.myIndex:
            ress = '* %s *' % ress[2:]
            decs = '* %s *' % decs[2:]
        playerpaths += decs + '\n'
        playerguessing += ress + '\n'
    for dec in stats.majorityLine:
        majoritypath += '1' if dec else '0'
    if stats.myIndex < 0:
        majoritypath += '\n\nMy decision line:\n-----------------\n- '
        for dec in stats._myDecisions:
            majoritypath += '1' if dec else '0'
    print('''
Position:
---------
%d / %d

Majority decision line:
-----------------------
- %s

Decision lines:
---------------
%s
Alignment with majority (Score):
--------------------------------
%s''' % (stats.myIndex+1, players, majoritypath, playerpaths, playerguessing[:-1]))

Paul Thomann

Posted 2014-07-08T20:18:06.290

Reputation: 346

1

The probability gambler, Python

This guy checks which side has gotten most total votes and chooses that side.

import sys
 good, evil = 0, 0
 if len(sys.argv) == 1: print "good"
 else:
     for round in sys.argv[1:]:
         for player in round:
             if player == "0":
                 evil += 1
             elif player == "1":
                good += 1
     if good > evil: print "good"
     else: print "evil"

Run as

 python probabilitygambler.py arg1 arg2... 

Mathetic

Posted 2014-07-08T20:18:06.290

Reputation: 103

1

Tommy

This might not be pinball but I still postulate you can play while being deaf and blind. The devil helped him not be dumb, however, for the first few votes.

package Humans;

public class Tommy extends Human {
public String takeSides(String history) {
    int players = 0;
    while(history.length()>players && history.charAt(history.length()-players-1) != ',')   {
        players++;
    }

    String NotSoSecret = "0000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111101010101010101010101010101010101010101010101010101010101010101010101010101010010010010010010010010010010010010010010010010010010010010010010010010010011011011011011011011011011011011011011011011011011011011010110110110110110100010001000100010001000100010001000100010001000100010001000100010001000100010001110111011101110111011101110111011101110111011101110111011101110111101110111011100000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111010101010101010101010101010101010101010101010101010101010101010101010101010100100100100100100100100100100100100100100100100100100100100100100100100100110110110110110110110110110110110110110110110110110110110101101101101101101000100010001000100010001000100010001000100010001000100010001000100010001000100011101110111011101110111011101110111011101110111011101110111011101111011101110111";

    if (NotSoSecret.charAt((history.length()+1)/(players+1))==1){
        return "good";
    }

    return "evil";

}}

kaine

Posted 2014-07-08T20:18:06.290

Reputation: 536

1

ConspiracyTheoristBot

This Tin-Foil cap toting crackpot uses the input to search for government conspiracies. If he finds one, then obviously the government has been subverting the will of the majority, and the minority is the safest place to be. Otherwise, he hides in the crowd of the majority.

public class ConspiracyTheorist extends Human{
    public String takeSides(String input) {
        if (theAnswer(input) || terror(input) 
                || beastly(input) || aliens(input) //number stuff
                || findMeaning(input, "alien") || findMeaning(input, "death") 
                || findMeaning(input, "jfk") || //Keywords of Conspiracy
                findMeaning(input, "plane") || findMeaning(input, "god") 
                || findMeaning(input, "devil") ||
                findMeaning(input, "blackhelecopter")) 
                      return printMin(input);
        return printMax(input);
    }

    //prints the current overall minority
    public String printMin(String input) {
        if (input.split("1").length > input.split("0").length) return "evil";
        return "good";
    }

    //prints the current overall majority
    public String printMax(String input) {
        if (input.split("1").length > input.split("0").length) return "good";
        return "evil";
    }

    //If the ultimate answer shows up in the input,
    //obviously the government is up to something and we
    //should join the minority
    public boolean theAnswer(String input) {
        return numerologist(input, "42");
    }

    //Area 51? Its the government! Run!
    public boolean aliens(String input) {
        return numerologist(input, "51");
    }

    //They did plan it, I'm sure of it.
    public boolean terror(String input) {
        return numerologist(input, "9") ||
                numerologist(input, "11") ||
                numerologist(input, "911");
    }

    //The end times are upon us!
    public boolean beastly(String input) {
        return numerologist(input, "666");
    }

    //if we can find some hidden words in the input,
    //obviously the government is up to something and we
    //should join the minority
    public boolean findMeaning(String input, String lookFor) {
        //split the input into bytes
        String[] bit8 = input.replace(",", "").split("(?<=\\G.{8})");
        //convert from binary to string
        String newWord = "";
        for (String b : bit8) {
            char c = (char) (int) Integer.valueOf(b, 2);
            if (Character.isLetterOrDigit(c)) newWord += c;
        }
        if (newWord.toLowerCase().contains(lookFor.toLowerCase())) return true;
        return false;
    }

    // The number crunching behind the tin foil hat
    public boolean numerologist(String input, String num) {
        String[] comcount = input.split(",");
        if ((comcount.length + "").contains(num)) return true;
        String[] onecount = input.split("1");
        if ((onecount.length + "").contains(num)) return true;
        String[] nullcount = input.split("0");
        if ((nullcount.length + "").contains(num)) return true;
        return false;
    }
}

Stranjyr

Posted 2014-07-08T20:18:06.290

Reputation: 111

0

Monkey With a Typewriter

Somebody let him loose on my computer! He's not quite quick enough at typing, so he guesses after .9s seconds...

import time
import random

startTime = time.time()
letters = 'abcdefghijklmnopqrstuvwxyz'

typed = "" 
it = 0

while (1):
    typed += random.choice(letters)
    outputStr = typed[-4:]

    # Oook!
    if (outputStr == "good" or outputStr == "evil"):
        break

    # Oook! OOOK! OOOOOOK!
    if (typed[-7:] == "bannana"):
        outputStr = "bannana"
        break

    if (it % 100 == 0):
        # Eeek!
        currentTime = time.time()
        if (currentTime - startTime > .9):
            outputStr = "good" if random.random() < .5 else "evil"
            break

    it += 1

print outputStr

python MonkeyWithATypeWriter.py

Jez

Posted 2014-07-08T20:18:06.290

Reputation: 109

This submission randomly times out once every 4-5 times it gets called. – Rainbolt – 2014-07-19T06:39:27.647

0

Logical

My code chooses its side by probabilities or by random if there isn't enough data.

C++:

#include <iostream>
#include <time.h>

using namespace std;

int main(int argc, char* argv2[])
{
    srand(time(0));
bool evil = false;
if(argc == 1) {
    if(rand() % 2 == 1) evil = true;
} else {

    char* argv = argv2[1];
    int length = strlen(argv);
    int rounds = 0, goods[1000], evils[1000];

    for(int i = 0; i < 1000; i++){

        goods[i] = 0;
        evils[i] = 0;
    }

    for(int i = 0; i < length; i++) {

        if(argv[i] == ',') {
            rounds++;
        } else if(argv[i] == '1') {
            goods[rounds]++;
        } else {
            evils[rounds]++;
        }
    }
    rounds++;
    cout << rounds << endl;
    if(rounds < 2) {

        if(rand() % 2 == 1) evil = true;
    } else {

        int changes[10]; //0-4 no change, 5-9 change
        int changeBorders[5], index = 0, difference = 0;
        fill_n(changes, 10, 0);
        fill_n(changeBorders, 5, 0);
        changeBorders[0] = (goods[0]+evils[0])/6;
        changeBorders[1] = changeBorders[0]*2;
        changeBorders[2] = changeBorders[0]*3;
        changeBorders[3] = changeBorders[0]*4;
        changeBorders[4] = changeBorders[0]*5;
        for(int i = 0; i < rounds-1; i++) {

            index = 0;

            if((goods[i] > evils[i] && goods[i+1] < evils[i+1]) || (goods[i] < evils[i] && goods[i+1] > evils[i+1])) index += 5;
            difference = abs(goods[i]-evils[i]);
            if(difference < changeBorders[0]) {//do nothing
            } else if(difference < changeBorders[1]) index++;
            else if(difference < changeBorders[2]) index += 2;
            else if(difference < changeBorders[3]) index += 3;
            else if(difference < changeBorders[4]) index += 4;
            changes[index]++;
        }

        difference = abs(goods[rounds-1]-evils[rounds-1]);
        if(difference < changeBorders[0]) index = 0;
        else if(difference < changeBorders[1]) index = 1;
        else if(difference < changeBorders[2]) index = 2;
        else if(difference < changeBorders[3]) index = 3;
        else if(difference < changeBorders[4]) index = 4;

        if(goods[rounds-1] > evils[rounds-1]) evil = false;
        else evil = true;

        if(changes[index] == 0 && changes[index+5] == 0) {
            if(rand() % 2 == 1) evil = true;
        } else if(changes[index+5] == 0) evil = !evil;
        else if(changes[index] == 0) { //do nothing
        } else if(changes[index] < changes[index+5]) evil = !evil;
    }
}

cout << (evil ? "evil" : "good") << endl;

return 0;

}

EXE: http://www.filedropper.com/goodandevil

Usage: Windows command line with "goodandevil.exe [vote history here]".

Hoxy

Posted 2014-07-08T20:18:06.290

Reputation: 1

0

FBI (Ruby)

print %w&good evil&[ARGV.map{|w|w.each_char.reject{|r|%r{,}=~r}.shuffle.pop.to_i}[%*#{Math.sqrt(5/2)}*.to_i-1]]

The FBI uses codes to hide intent. What it dose is:

Something you can find out yourself! Why would I go to this extent to hide the meaning of code then tell you what it means in a spoiler right after?

MegaTom

Posted 2014-07-08T20:18:06.290

Reputation: 3 787