112
\$\begingroup\$

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.

\$\endgroup\$
  • 2
    \$\begingroup\$ Comments purged, as they were obsolete and at request of OP. Please notify me of any comments that need to be undeleted. \$\endgroup\$ – Doorknob Jul 16 '14 at 11:28
  • 7
    \$\begingroup\$ Woah, OP changes his username. Ok, so when will the result be displayed? \$\endgroup\$ – justhalf Jul 18 '14 at 6:52
  • 6
    \$\begingroup\$ @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 \$\endgroup\$ – tomsmeding Jul 19 '14 at 18:54
  • 3
    \$\begingroup\$ @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. \$\endgroup\$ – Rainbolt Jul 21 '14 at 12:03
  • 6
    \$\begingroup\$ One 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. \$\endgroup\$ – Rainbolt Jul 21 '14 at 18:57

92 Answers 92

6
\$\begingroup\$

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

}
\$\endgroup\$
6
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ 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 \$\endgroup\$ – Rainbolt Jul 19 '14 at 7:45
  • \$\begingroup\$ @Rainbolt Sorry, you have to add the line #include <cstdlib> before everything. That'll work. — I edited the post. \$\endgroup\$ – tomsmeding Jul 19 '14 at 7:47
  • 2
    \$\begingroup\$ I 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. \$\endgroup\$ – Rainbolt Jul 19 '14 at 7:57
  • \$\begingroup\$ @Rainbolt Cool! I am curious as to the results \$\endgroup\$ – tomsmeding Jul 19 '14 at 7:59
5
\$\begingroup\$

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

}
\$\endgroup\$
  • 12
    \$\begingroup\$ Somehow not as evil as HAL. \$\endgroup\$ – SomeGuy Jul 9 '14 at 16:26
  • 5
    \$\begingroup\$ I like it how the Angel uses Python, while the Demon uses Java. \$\endgroup\$ – Alex Jul 14 '14 at 14:42
5
\$\begingroup\$

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";
    }
}
\$\endgroup\$
5
\$\begingroup\$

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%
\$\endgroup\$
  • 2
    \$\begingroup\$ Upvote for making an obvious idea more creative by using Batch. And you've given me an idea to try... \$\endgroup\$ – trlkly Jul 12 '14 at 21:57
5
\$\begingroup\$

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();
      }
   }
\$\endgroup\$
  • \$\begingroup\$ Where do I need to submit the .exe? \$\endgroup\$ – IEatBagels Jul 11 '14 at 13:23
  • 1
    \$\begingroup\$ You 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. \$\endgroup\$ – tomsmeding Jul 11 '14 at 13:25
  • \$\begingroup\$ I don't know how -.-' Would you mind telling me? \$\endgroup\$ – IEatBagels Jul 11 '14 at 13:26
  • \$\begingroup\$ Syntax highlighting is overrated, IMO. I have yet to find a post in which it is absolutely necessary. \$\endgroup\$ – Kyle Kanos Jul 11 '14 at 13:42
  • \$\begingroup\$ @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. \$\endgroup\$ – tomsmeding Jul 11 '14 at 13:52
5
\$\begingroup\$

The clock

Lazy entry. Changes side every second.

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

Run with node clock.js

\$\endgroup\$
  • \$\begingroup\$ A round will definitely take more than a second, so it won't really be a "clock"... Fun idea though :) \$\endgroup\$ – tomsmeding Jul 16 '14 at 12:25
5
\$\begingroup\$

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

\$\endgroup\$
5
\$\begingroup\$

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.

\$\endgroup\$
5
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ Since you have the exact same name as an older submission by @Trimsty, I edited your title. \$\endgroup\$ – Rainbolt Jul 20 '14 at 6:02
  • \$\begingroup\$ @Rainbolt His is actually older. Whoops. (you can do whatever you like with my answer) \$\endgroup\$ – cjfaure Jul 20 '14 at 19:15
  • \$\begingroup\$ @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. \$\endgroup\$ – Rainbolt Jul 20 '14 at 19:17
  • \$\begingroup\$ @Rainbolt Aw, how nice :D Well, no further action is technically necessary I guess, unless he wants to. \$\endgroup\$ – cjfaure Jul 20 '14 at 19:20
4
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ You need to provide a command that can be used to run your program. See the "Deliverables" section of the challenge for more information. \$\endgroup\$ – Rainbolt Jul 8 '14 at 22:06
  • \$\begingroup\$ Edited for clarity, I'd thought "Runs the same way as" would be sufficient. \$\endgroup\$ – histocrat Jul 8 '14 at 22:13
  • 2
    \$\begingroup\$ It 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! \$\endgroup\$ – Rainbolt Jul 8 '14 at 22:53
4
\$\begingroup\$

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
\$\endgroup\$
4
\$\begingroup\$

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
\$\endgroup\$
4
\$\begingroup\$

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

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – trlkly Jul 12 '14 at 23:36
  • \$\begingroup\$ 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. \$\endgroup\$ – trlkly Jul 13 '14 at 22:48
4
\$\begingroup\$

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";
    }
}
\$\endgroup\$
  • \$\begingroup\$ two things: history.length() includes the commas. and this is basically the same as codegolf.stackexchange.com/a/33152/8478, but I don't think that's a problem unless our submissions tie for the 1st place ;) \$\endgroup\$ – Martin Ender Jul 9 '14 at 13:35
  • \$\begingroup\$ @m.buettner I assume you mean history and not history.length(), since the latter returns an integer, not a string. \$\endgroup\$ – FreeAsInBeer Jul 9 '14 at 13:50
  • \$\begingroup\$ Well yes, what I'm saying is history.length() counts commas, too. \$\endgroup\$ – Martin Ender Jul 9 '14 at 13:54
  • 1
    \$\begingroup\$ Yep. 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. \$\endgroup\$ – FreeAsInBeer Jul 9 '14 at 13:54
4
\$\begingroup\$

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

}
\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Sparr Jul 9 '14 at 18:00
4
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ 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). \$\endgroup\$ – Martin Ender Jul 9 '14 at 12:03
  • \$\begingroup\$ 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? \$\endgroup\$ – plannapus Jul 9 '14 at 12:07
  • 1
    \$\begingroup\$ @plannapus Yes and yes \$\endgroup\$ – SBoss Jul 9 '14 at 12:07
  • \$\begingroup\$ Ok :) So you made sure it doesn't break the rule then. \$\endgroup\$ – plannapus Jul 9 '14 at 12:09
  • 1
    \$\begingroup\$ @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.) \$\endgroup\$ – Martin Ender Jul 9 '14 at 12:59
3
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ Yes, there already was 1. here Cool, though; I like JS :P \$\endgroup\$ – tomsmeding Jul 12 '14 at 6:02
3
\$\begingroup\$

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

\$\endgroup\$
  • \$\begingroup\$ They should probably have different filenames... most people will want to put them in the same folder. \$\endgroup\$ – Brilliand Jul 12 '14 at 3:08
  • \$\begingroup\$ @Brilliand Sure. \$\endgroup\$ – isaacg Jul 12 '14 at 3:21
  • \$\begingroup\$ Wow I just noticed 2**players. 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 2**players % sys.maxint was small. \$\endgroup\$ – Rainbolt Jul 15 '14 at 3:03
  • \$\begingroup\$ @Rusher Yeah, in retrospect that was a terrible idea. Feel free to remove this answer from the actual submission group. \$\endgroup\$ – isaacg Jul 15 '14 at 4:28
  • \$\begingroup\$ 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? \$\endgroup\$ – Andris Jul 16 '14 at 6:53
3
\$\begingroup\$

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";
        }
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Throwing an exception in the first round disqualifies this every time. \$\endgroup\$ – Brilliand Jul 13 '14 at 18:27
3
\$\begingroup\$

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

\$\endgroup\$
  • \$\begingroup\$ Clever Girl.... \$\endgroup\$ – Stranjyr Jul 29 '14 at 19:16
3
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ > 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>' \$\endgroup\$ – Rainbolt Jul 15 '14 at 3:18
  • \$\begingroup\$ 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). \$\endgroup\$ – Rainbolt Jul 15 '14 at 3:20
  • \$\begingroup\$ @Rusher Then you're using a deprecated version of Ruby. I fixed it so it should work anyhow ("casting" the enumerables to arrays). \$\endgroup\$ – daniero Jul 15 '14 at 13:49
  • \$\begingroup\$ 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. \$\endgroup\$ – Rainbolt Jul 15 '14 at 13:55
  • \$\begingroup\$ @Rusher Are you using 1.9.3? (1.8.7 is "not really ruby" anymore: ruby-lang.org/en/news/2013/06/30/we-retire-1-8-7) Two seconds, and I'll sort it out. \$\endgroup\$ – daniero Jul 15 '14 at 13:58
3
\$\begingroup\$

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])
\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Rainbolt Jul 19 '14 at 7:06
  • \$\begingroup\$ @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. \$\endgroup\$ – zch Jul 19 '14 at 9:32
3
\$\begingroup\$
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";
    }
}
\$\endgroup\$
  • \$\begingroup\$ 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). \$\endgroup\$ – Rainbolt Jul 19 '14 at 8:44
  • \$\begingroup\$ @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. \$\endgroup\$ – Axoren Jul 19 '14 at 8:45
  • 1
    \$\begingroup\$ You were off by one letter lol. chatat(). I fixed it. \$\endgroup\$ – Rainbolt Jul 19 '14 at 8:52
  • 1
    \$\begingroup\$ Oh such dishonor... I must commit Sudoku. Thanks, lol. \$\endgroup\$ – Axoren Jul 19 '14 at 8:53
  • \$\begingroup\$ Kay. GL with your sudoku. \$\endgroup\$ – Rainbolt Jul 19 '14 at 9:05
3
\$\begingroup\$

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

\$\endgroup\$
  • 1
    \$\begingroup\$ I 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. \$\endgroup\$ – Stuart P. Bentley Jul 11 '14 at 17:54
  • \$\begingroup\$ 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. \$\endgroup\$ – Rainbolt Jul 19 '14 at 8:33
  • \$\begingroup\$ I was using lua 5.2.1 at 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. \$\endgroup\$ – Rainbolt Jul 19 '14 at 8:48
  • \$\begingroup\$ Whoops, I refactored the variable names (from 'l' for 'last' to 'h' for 'history') and didn't update all the instances. Fixed. \$\endgroup\$ – Stuart P. Bentley Jul 19 '14 at 8:50
  • \$\begingroup\$ 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. \$\endgroup\$ – Rainbolt Jul 19 '14 at 8:55
3
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ @plannapus I'll do that in the next few days... \$\endgroup\$ – jimmy23013 Jul 11 '14 at 14:37
  • \$\begingroup\$ @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. \$\endgroup\$ – jimmy23013 Jul 16 '14 at 3:22
  • \$\begingroup\$ that's perfect like that! Thanks for the explanation! +1 \$\endgroup\$ – plannapus Jul 16 '14 at 6:34
  • \$\begingroup\$ 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? \$\endgroup\$ – Rainbolt Jul 19 '14 at 7:01
  • \$\begingroup\$ @Rainbolt sourceforge.net/projects/cjam It is invented by someone in this site for code golfing. It isn't widely used. \$\endgroup\$ – jimmy23013 Jul 19 '14 at 7:45
2
\$\begingroup\$

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

}
\$\endgroup\$
  • 2
    \$\begingroup\$ I 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. \$\endgroup\$ – Rainbolt Jul 12 '14 at 4:22
2
\$\begingroup\$

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...

\$\endgroup\$
  • 1
    \$\begingroup\$ I 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. \$\endgroup\$ – Rainbolt Jul 15 '14 at 2:37
2
\$\begingroup\$

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";
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.