Lowest unique bid auction

22

6

Thanks for all the entries, the deadline has now passed and the final scores are at the end of the question.
Congratulations to PhiNotPi on a fairly comprehensive victory.

This is a challenge, the aim of which is to create a program which wins more often than any of its opponents in a lowest unique bid auction.

Input

As input the program will receive all the previous rounds' bidding, one round per line, all bids separated by spaces as follows:

10 4 12 11 12 4 7 3 3
1 2 9 15 1 15 15 9 3
3 21 6 4 3 8 6 13 1

Each column of the input represents the bidding of one bot. The first column is the receiving program's bids, while the rest are in a randomly generated order. Thanks to hammar and Peter Taylor for their input.

Input is provided as the one and only command-line (multi-line) argument to your program:

./test1 '1 2
3 4
5 6
1 2'

This means that your program will need to be runnable from the command-line. Please give an example of invocation as part of your answer.

In the first round only as a means of letting you know how many bots you're up against, the input will be a line of 0s - one for each bot.

Output

Your program should output its bid as an integer in the range 1 to 100 (inclusive).

Scorer Program

This is my scoring program - any suggestions for additions, improvements or bug fixes would be welcomed.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NUMROUNDS 10
#define NUMBOTS 4
#define MAXINPUTSIZE 10000
#define MAXFILENAMESIZE 100

int main()
{
    int i,j,a,b,winner;
    FILE *fp;
    char bots[NUMBOTS][MAXFILENAMESIZE]={"onesconfident","random100","random20","random5"};
    char openstring[MAXFILENAMESIZE+MAXINPUTSIZE+3];
    char input[MAXINPUTSIZE];
    char buff[5];
    int shuffle[NUMBOTS],auction[100],lowestbid[NUMBOTS]={[0 ... NUMBOTS-1]=101};
    static int guesses[NUMBOTS][NUMROUNDS];
    static int scores[NUMBOTS],totalwinbids[NUMBOTS];

    srand(time(NULL));

    for(i=0;i<NUMROUNDS;i++)
    {
        /*blank the auction bids for the next round */
        for(a=0;a<100;a++)
        {
            auction[a]=9999;
        }

        /*loop through the bots sending the input and storing their output */
        for(j=0;j<NUMBOTS;j++)
        {
            /*Fisher-Yates shuffle */
            for(b=0;b<NUMBOTS;b++)
            {
                shuffle[b]=(b+j)%NUMBOTS;/*put current bot at index 0 */
            }
            for(b=NUMBOTS-1;b>1;b--)
            {
                int z=rand()%(b-1)+1;/*make sure shuffle leaves index 0 alone */
                int t=shuffle[b];
                shuffle[b]=shuffle[z];
                shuffle[z]=t;
            }

            /*generate the input for the bots */
            strcpy(input,"'");
            if(i==0)
            {
                for(b=0;b<NUMBOTS;b++)
                {
                    if(b!=0)
                        sprintf(input,"%s 0",input);
                    else
                        sprintf(input,"%s0",input);
                }
            }
            else
            {
                for(a=0;a<i;a++)
                {
                    for(b=0;b<NUMBOTS;b++)
                    {
                        if(b!=0)
                            sprintf(input,"%s %d",input,guesses[shuffle[b]][a]);
                        else
                            sprintf(input,"%s%d",input,guesses[shuffle[b]][a]);
                    }
                    if(a!=i-1)
                        strcat(input,"\n");
                }
            }
            strcat(input,"'");

            sprintf(openstring,"%s %s",bots[j],input);
            fp=popen(openstring,"r");

            fgets(buff,3,fp);
            fflush(NULL);
            pclose(fp);
            guesses[j][i]=atoi(buff);

            /*add the bid to the auction, eliminating any duplicates */
            if(auction[atoi(buff)-1]!=9999)
                auction[atoi(buff)-1]=9998;
            else
                auction[atoi(buff)-1]=j;
        }

        winner=9999;
        /*add one to the score of the winning bot */
        for(a=0;a<100;a++)
        {
            if(auction[a]!=9998 && auction[a]!=9999)
            {
                winner=auction[a];
                scores[winner]+=1;
                totalwinbids[winner]+=guesses[winner][i];
                if(guesses[winner][i]<lowestbid[winner])
                    lowestbid[winner]=guesses[winner][i];
                break;
            }
        }

        /*output this round's bids and the winning bot's name */
        strcpy(input,"");
        for(b=0;b<NUMBOTS;b++)
        {
            if(strcmp(input,"")!=0)
                sprintf(input,"%s %d",input,guesses[b][i]);
            else
                sprintf(input,"%d",guesses[b][i]);
        }
        if(winner!=9999)
            printf("%s %s\n",input,bots[winner]);
        else
            printf("%s No winner\n",input);
    }

    /*output final scores */
    printf("\nResults:\n");
    printf("Bot\tScore\tTotal\tLowest\n");
    for(a=0;a<NUMBOTS;a++)
    {
        printf("%s\t%d\t%d\t%d\n",bots[a],scores[a],totalwinbids[a],lowestbid[a]);
    }

    return 0;
}

Test players

One's confident Always bids 1.

#include <stdio.h>

int main()
{
    printf("1");
    return 0;
}

Random100 Bids at random over the entire range

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    srand(getpid());
    printf("%d",rand()%100+1);
    return 0;
}

Random20 Bids at random between 1 and 20

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    srand(getpid());
    printf("%d",rand()%20+1);
    return 0;
}

Random5 Bids at random between 1 and 5

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    srand(getpid());
    printf("%d",rand()%5+1);
    return 0;
}

Example run-through:

1 38 5 2 onesconfident
1 66 13 5 onesconfident
1 94 1 3 random5
1 22 9 1 random20
1 50 17 4 onesconfident
1 78 5 2 onesconfident
1 6 13 5 onesconfident
1 34 1 3 random5
1 62 9 1 random20
1 90 17 4 onesconfident

Results:
Bot Score   Total   Lowest
onesconfident   6   6   1
random100   0   0   101
random20    2   18  9
random5 2   6   3

These players are for testing purposes only. They will NOT be included in the competition. You can enter as many bots as you wish, so if anyone does enter a bot that only guesses 1, you can enter another that does the same to render it useless.

Winner

The winning bot in each round is the one which gives the lowest unique bid. So given a round in which the following bids are made: 1 1 3 5 2 3 6 3 2 8 7 the winner would be the bot that bid 5 because the 1s, 2s and 3s are not unique.

The winner of the competition will be the program which wins the most times after 100 rounds. In case of a tie the total of the winning bids will be used as a tie-breaker, and in the event of that also being a tie, the lowest winning bid will be used as a further tie-breaker. These scoring factors are all output by the scoring program.

I will run the scoring program on all working programs that have been entered 2 weeks from today (18th February now extended to 11pm(GMT) on the 20th of February). I'll upvote all working entries and accept the winner of my scoring run.

Final scoring run

1 9 3 2 1 6 4 3 6 8 7 10 26 6 10 5 26 2 5 8 8 5 7 6 42 1 ./phinotpi2
1 11 4 2 1 4 9 20 6 8 7 6 26 4 8 4 26 2 5 8 8 5 7 7 42 1 ./phinotpi2
1 7 9 2 1 4 3 20 6 8 7 6 7 4 8 9 26 2 5 8 8 5 4 9 42 3 node minitech1.js
1 13 20 2 1 3 3 20 6 8 7 7 9 6 8 20 26 2 5 8 8 5 9 9 42 3 ./dirichlet
1 12 13 2 1 1 3 20 6 8 7 7 9 6 9 13 26 2 5 8 8 5 20 9 42 3 ./dirichlet
1 2 4 2 1 1 3 20 6 8 7 7 9 6 9 12 26 2 5 8 8 5 13 9 42 3 python blazer1.py
1 11 4 2 1 4 3 20 6 8 7 6 7 4 8 4 26 2 5 8 8 5 12 9 42 3 ./celtschk
1 3 4 2 1 1 3 20 6 8 7 6 7 4 8 9 26 2 5 8 8 5 4 9 42 3 node minitech1.js
1 7 4 2 1 1 3 20 6 8 7 9 26 6 7 20 26 2 5 8 8 5 9 9 42 10 ./phinotpi2
1 9 9 2 1 3 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 20 9 42 10 ./phinotpi2
1 13 4 2 1 3 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 scala Schwarzenbeck
1 12 20 2 1 1 3 20 6 8 7 6 20 4 8 7 26 2 5 8 8 5 4 9 42 10 ./phinotpi2
1 10 3 2 1 2 4 20 6 8 7 6 9 3 9 3 26 2 5 8 8 5 7 9 42 10 ./phinotpi2
1 6 9 2 1 4 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 scala Schwarzenbeck
1 8 4 2 1 3 3 20 6 8 7 6 20 4 8 7 26 2 5 8 8 5 4 9 42 10 ./celtschk
1 2 13 2 1 3 3 20 6 8 7 9 20 6 8 9 26 2 5 8 8 5 7 9 42 10 ruby1.9 strategist.rb
1 2 4 2 1 3 3 20 6 8 7 7 10 6 9 10 26 2 5 8 8 5 9 9 42 10 python blazer1.py
1 3 13 2 1 4 3 20 6 8 7 6 7 4 8 4 26 2 5 8 8 5 10 9 42 10 ./celtschk
1 4 4 2 1 3 3 20 6 8 7 6 7 4 8 9 26 2 5 8 8 5 4 9 42 10 ruby1.9 strategist.rb
1 4 9 2 1 4 3 20 6 8 7 7 9 6 8 10 26 2 5 8 8 5 9 9 42 10 ./phinotpi2
1 11 7 2 1 1 4 20 6 8 7 6 7 3 8 3 26 2 5 8 8 5 10 9 42 10 ./phinotpi2
1 6 4 2 1 3 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 scala Schwarzenbeck
1 13 7 2 1 1 3 20 6 8 7 6 20 4 8 7 26 2 5 8 8 5 4 9 42 10 ./phinotpi2
1 7 4 2 1 4 4 20 6 8 7 6 20 3 8 3 26 2 5 8 8 5 7 9 42 10 ./celtschk
1 13 3 2 1 1 4 20 6 8 7 6 7 3 8 9 26 2 5 8 8 5 3 9 42 10 ./phinotpi2
1 3 4 2 1 3 3 20 6 8 7 6 7 4 8 4 26 2 5 8 8 5 9 9 42 10 ruby1.9 strategist.rb
1 5 4 2 1 2 3 20 6 8 7 6 7 4 8 10 26 2 5 8 8 5 4 9 42 10 ./phinotpi2
1 6 3 2 1 3 4 20 6 8 7 6 7 3 8 3 26 2 5 8 8 5 10 9 42 10 ./phinotpi2
1 10 20 2 1 1 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 scala Schwarzenbeck
1 10 3 2 1 4 3 20 6 8 7 6 20 4 8 7 26 2 5 8 8 5 4 9 42 10 ./celtschk
1 12 4 2 1 1 3 20 6 8 7 9 20 6 8 9 26 2 5 8 8 5 7 9 42 10 ./phinotpi2
1 5 3 2 1 1 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 9 9 42 10 ./phinotpi2
1 13 3 2 1 4 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 scala Schwarzenbeck
1 6 9 2 1 4 3 20 6 8 7 6 20 4 8 7 26 2 5 8 8 5 4 9 42 10 ./phinotpi2
1 5 4 2 1 2 4 20 6 8 7 6 20 3 8 3 26 2 5 8 8 5 7 9 42 10 ./celtschk
1 12 3 2 1 3 4 20 6 8 7 6 7 3 8 9 26 2 5 8 8 5 3 9 42 10 ./phinotpi2
1 10 7 2 1 2 3 20 6 8 7 6 7 4 8 4 26 2 5 8 8 5 9 9 42 10 ./phinotpi2
1 9 10 2 1 4 9 20 6 8 7 4 6 3 8 3 26 2 5 8 8 5 4 9 42 10 scala Schwarzenbeck
1 9 20 2 1 4 4 20 6 8 7 6 20 3 8 7 26 2 5 8 8 5 3 9 42 10 ruby1.9 strategist.rb
1 6 3 2 1 3 3 20 6 8 7 9 10 6 9 10 26 2 5 8 8 5 7 9 42 10 node minitech1.js
1 13 3 2 1 3 3 20 6 8 7 7 10 6 8 20 26 2 5 8 8 5 10 9 42 11 ./celtschk
1 3 3 2 1 1 3 20 6 8 7 7 26 6 9 9 26 2 5 8 8 5 20 9 42 11 ruby1.9 strategist.rb
1 5 20 2 1 2 3 20 6 8 7 7 11 6 9 11 26 2 5 8 8 5 9 9 42 11 ./phinotpi2
1 7 3 2 1 4 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 11 9 42 11 node minitech1.js
1 7 3 2 1 1 4 20 6 8 7 6 7 3 8 20 26 2 5 8 8 5 3 9 42 10 ./phinotpi2
1 8 4 2 1 4 3 20 6 8 7 6 7 4 8 4 26 2 5 8 8 5 20 9 42 10 ./phinotpi2
1 2 3 2 1 3 9 20 6 8 7 4 6 3 8 3 26 2 5 8 8 5 4 9 42 10 scala Schwarzenbeck
1 4 13 2 1 3 4 20 6 8 7 6 20 3 7 7 26 2 5 8 8 5 3 9 42 10 ./celtschk
1 8 3 2 1 3 3 20 6 8 7 9 20 6 8 9 26 2 5 8 8 5 7 9 42 10 ruby1.9 strategist.rb
1 9 10 2 1 2 3 20 6 8 7 7 10 6 9 10 26 2 5 8 8 5 9 9 42 10 ./phinotpi2
1 10 20 2 1 1 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 10 9 42 10 ./phinotpi2
1 9 4 2 1 1 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 scala Schwarzenbeck
1 11 20 2 1 4 3 20 6 8 7 6 20 4 8 7 26 2 5 8 8 5 4 9 42 10 ./phinotpi2
1 4 9 2 1 3 4 20 6 8 7 6 9 3 9 3 26 2 5 8 8 5 7 9 42 10 ruby1.9 strategist.rb
1 5 3 2 1 4 4 20 6 8 7 6 7 3 8 10 26 2 5 8 8 5 3 9 42 10 ./celtschk
1 7 4 2 1 3 3 20 6 8 7 7 9 6 8 9 26 2 5 8 8 5 10 9 42 10 python blazer1.py
1 4 9 2 1 1 3 20 6 8 7 6 7 4 8 4 26 2 5 8 8 5 9 9 42 10 ./phinotpi2
1 8 4 2 1 3 9 20 6 8 7 4 6 3 8 3 26 2 5 8 8 5 4 9 42 10 scala Schwarzenbeck
1 10 9 2 1 3 4 20 6 8 7 6 20 3 8 7 26 2 5 8 8 5 3 9 42 10 ./phinotpi2
1 4 20 2 1 1 3 20 6 8 7 6 20 4 8 4 26 2 5 8 8 5 7 9 42 10 ./phinotpi2
1 5 3 2 1 2 9 20 6 8 7 4 6 3 9 3 26 2 5 8 8 5 4 9 42 10 scala Schwarzenbeck
1 2 4 2 1 1 4 20 6 8 7 6 20 3 8 7 26 2 5 8 8 5 3 9 42 10 ./celtschk
1 10 12 2 1 1 3 20 6 8 7 9 20 6 8 9 26 2 5 8 8 5 7 9 42 10 ./phinotpi2
1 9 4 2 1 4 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 9 9 42 10 ruby1.9 strategist.rb
1 11 3 2 1 3 4 20 6 8 7 6 7 3 8 10 26 2 5 8 8 5 3 9 42 10 ./phinotpi2
1 8 4 2 1 1 3 20 6 8 7 6 7 4 8 4 26 2 5 8 8 5 10 9 42 10 ./phinotpi2
1 13 9 2 1 4 9 20 6 8 7 4 6 3 8 3 26 2 5 8 8 5 4 9 42 10 scala Schwarzenbeck
1 2 9 2 1 3 4 20 6 8 7 6 20 3 8 7 26 2 5 8 8 5 3 9 42 10 ./phinotpi2
1 8 3 2 1 2 3 20 6 8 7 6 20 4 8 4 26 2 5 8 8 5 7 9 42 10 ./celtschk
1 3 3 2 1 4 3 20 6 8 7 6 7 4 8 9 26 2 5 8 8 5 4 9 42 10 ruby1.9 strategist.rb
1 10 4 2 1 1 3 20 6 8 7 7 9 6 8 10 26 2 5 8 8 5 9 9 42 10 ./phinotpi2
1 3 9 2 1 4 4 20 6 8 7 6 7 3 8 3 26 2 5 8 8 5 10 9 42 10 node minitech1.js
1 7 11 2 1 4 4 20 6 8 7 6 7 3 8 20 26 2 5 8 8 5 3 9 42 10 ./celtschk
1 8 3 2 1 2 3 20 6 8 7 7 9 6 8 9 26 2 5 8 8 5 20 9 42 10 ruby1.9 strategist.rb
1 3 10 2 1 3 3 20 6 8 7 7 10 6 9 10 26 2 5 8 8 5 9 9 42 10 node minitech1.js
1 8 4 2 1 1 3 20 6 8 7 7 10 6 8 20 26 2 5 8 8 5 10 9 42 11 ./phinotpi2
1 2 4 2 1 2 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 20 9 42 11 ruby1.9 strategist.rb
1 4 9 2 1 4 4 20 6 8 7 6 7 3 8 11 26 2 5 8 8 5 3 9 42 11 node minitech1.js
1 4 9 2 1 1 3 20 6 8 7 7 11 6 8 20 26 2 5 8 8 5 11 9 42 10 ./phinotpi2
1 2 7 2 1 1 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 20 9 42 10 ./phinotpi2
1 9 3 2 1 1 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 scala Schwarzenbeck
1 3 9 2 1 2 3 20 6 8 7 6 20 4 8 7 26 2 5 8 8 5 4 9 42 10 ruby1.9 strategist.rb
1 5 7 2 1 3 3 20 6 8 7 10 20 6 8 10 26 2 5 8 8 5 7 9 42 10 ./celtschk
1 8 10 2 1 4 3 20 6 8 7 7 10 6 9 9 26 2 5 8 8 5 10 9 42 10 ./phinotpi2
1 5 4 2 1 4 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 9 9 42 10 ruby1.9 strategist.rb
1 5 20 2 1 3 4 20 6 8 7 6 7 3 8 10 26 2 5 8 8 5 3 9 42 10 ./phinotpi2
1 11 20 2 1 2 3 20 6 8 7 6 7 4 8 4 26 2 5 8 8 5 10 9 42 10 ./phinotpi2
1 12 10 2 1 1 9 20 6 8 7 4 6 3 9 3 26 2 5 8 8 5 4 9 42 10 scala Schwarzenbeck
1 10 3 2 1 1 4 20 6 8 7 6 20 3 8 7 26 2 5 8 8 5 3 9 42 10 ./phinotpi2
1 9 4 2 1 4 3 20 6 8 7 6 20 4 8 4 26 2 5 8 8 5 7 9 42 10 ./phinotpi2
1 5 3 2 1 1 9 20 6 8 7 4 6 3 8 3 26 2 5 8 8 5 4 9 42 10 scala Schwarzenbeck
1 7 4 2 1 1 4 20 6 8 7 6 20 3 7 7 26 2 5 8 8 5 3 9 42 10 ./celtschk
1 11 7 2 1 3 3 20 6 8 7 9 20 6 8 9 26 2 5 8 8 5 7 9 42 10 ruby1.9 strategist.rb
1 13 10 2 1 1 3 20 6 8 7 7 10 6 9 10 26 2 5 8 8 5 9 9 42 10 ./phinotpi2
1 9 9 2 1 1 4 20 6 8 7 6 7 3 9 3 26 2 5 8 8 5 10 9 42 10 ./phinotpi2
1 7 9 2 1 3 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 ruby1.9 strategist.rb
1 13 7 2 1 4 3 20 6 8 7 6 7 4 8 10 26 2 5 8 8 5 4 9 42 10 ./phinotpi2
1 8 7 2 1 1 4 20 6 8 7 6 7 3 8 3 26 2 5 8 8 5 10 9 42 10 ./phinotpi2
1 12 3 2 1 1 9 20 6 8 7 4 6 3 8 4 26 2 5 8 8 5 3 9 42 10 scala Schwarzenbeck
1 13 7 2 1 2 3 20 6 8 7 6 20 4 8 7 26 2 5 8 8 5 4 9 42 10 ./phinotpi2

Results:
Bot                 Score   Total   Lowest
perl phinotpi1.pl           0   0   101
./dirichlet                 2   25  12
python blazer1.py           3   12  4
perl chef.pl ilmari2.chef   0   0   101
./brainfuck ilmari1.bf      0   0   101
./christophe1               0   0   101
./phinotpi2                 44  156 3
node minitech1.js           7   140 20
scala Mueller               0   0   101
scala Beckenbauer           0   0   101
scala Schwarzenbeck         15  105 7
./alice                     0   0   101
./bob                       0   0   101
./eve                       0   0   101
python joe.py               0   0   101
python copycat.py           0   0   101
python totalbots.py         0   0   101
perl healthinspector.pl     0   0   101
./mellamokb1                0   0   101
./mellamokb2                0   0   101
php eightscancel.php        0   0   101
php fivescancel.php         0   0   101
python copycat2.py          0   0   101
./celtschk                  14  126 9
./deepthought               0   0   101
ruby1.9 strategist.rb       15  152 10

Gareth

Posted 2012-02-04T01:00:53.370

Reputation: 11 678

1Hmm... with the rules written as they are, I could really spoil the game by entering 100 programs that each always bid a given number. – Ilmari Karonen – 2012-02-04T02:23:47.167

1Can you say two sentences, how the winning bot is chosen? I don't get it. – user unknown – 2012-02-04T05:15:00.527

@IlmariKaronen That's true, you could. But I'm trusting that people won't do that. I could limit the number of entries per person I suppose, but I think I'll only resort to that if any spoilers do come along. – Gareth – 2012-02-04T09:39:45.993

@userunknown I've tried to clarify how the auctions rounds work. – Gareth – 2012-02-04T09:45:23.430

Shouldn't the condition if(strcmp(input,"")!=0) always succeed given that you initialized input with "'"? – celtschk – 2012-02-04T10:36:51.877

@celtschk Yep, that's a bug. Thanks. It just meant that there was an extra space at the start of the input string for the bots. I've (hopefully) fixed it now. – Gareth – 2012-02-04T10:49:33.083

But now there should still be an initial space for all lines of the other bots because at that point input already is partially filled, right? – celtschk – 2012-02-04T11:00:33.377

@celtschk Oops. Yes, you're right. I'll fix that now. – Gareth – 2012-02-04T11:58:27.097

BTW, I just notice that with the chosen format, on the first call there's no way to tell how many bots there are (because there are no bids yet). Is this intentional? – celtschk – 2012-02-04T12:55:59.160

@celtschk Yes, it never occurred to me that anyone would need to know how many bots they were up against - just what they had bid in previous rounds. I don't want to change the input format now that the question has been posted. Maybe just bid 1 in the first round and do all the clever stuff for the other 99 rounds? – Gareth – 2012-02-04T13:06:29.383

Technically it wouldn't be a change to the spec to supply a list of blank lines - in fact, the spec does state that an argument will be supplied without making an exception for the case when it contains no numbers. – Peter Taylor – 2012-02-05T17:13:04.633

@celtschk @PeterTaylor I'll change the spec slightly so that on the first run it prints one 0 for every bot in the game. If this causes anyone problems for their implementation please let me know. – Gareth – 2012-02-05T22:34:22.630

could you perhaps, in the 'latest test run' section, provide which bot is in which column? – Blazer – 2012-02-08T23:10:44.267

They're in the order that follows in the list of scores - first column is phinotpi1.pl, second is dirichlet etc. Not sure I could format the columns with the names over them since there so many entries now - but if anyone has any ideas how to better format the test run for reading I'll be happy to implement them if I can. – Gareth – 2012-02-08T23:14:49.710

oh okay, I probably should have realized that – Blazer – 2012-02-08T23:16:34.630

@Gareth, have you recompiled Dirichlet recently? It shouldn't be submitting 1 any more, so unless you're still using the first version I need to debug. – Peter Taylor – 2012-02-14T20:12:24.927

@PeterTaylor I thought I had, but I've just double-checked and re-compiled. It bid 1 for the first 3 rounds of my quick test run. – Gareth – 2012-02-14T21:09:32.167

Hmm. Something's very wrong, because n and bits are never negative, so val is never negative and 2+val can never equal 1. – Peter Taylor – 2012-02-14T21:26:43.340

@PeterTaylor I've just run the program in isolation and it gives a 'Floating point error' whenever there are 2 lines of input or less. I assume my scorer program is seeing that and just returning 1. I'm not a C expert by any means so I'm not sure what's causing the error in the first place. – Gareth – 2012-02-14T23:17:27.730

@PeterTaylor Actually, looking at the program, is the line val = bits % n; valid if n is 0? For the first 3 rounds there will be 0, 0 and 1 newlines respectively so the line n \= 2; will set n to 0 if I'm following the program correctly. – Gareth – 2012-02-14T23:21:58.890

Aaaaaaah. My bug. Misread the spec, and was expecting one line per bot. – Peter Taylor – 2012-02-15T00:55:47.570

I have an idea.. but will probably take a day or two. Is it possible to get an extension, say until Monday night? – mellamokb – 2012-02-17T20:38:07.493

@mellamokb Yeah, don't see why not. I'll edit the question to reflect the new closing date. – Gareth – 2012-02-17T22:56:56.513

An extension to the deadline! But I was winning! – PhiNotPi – 2012-02-17T23:07:57.013

I have a suspicion as to what mallamokb is doing, so I have temporarily deleted my leading bot in order to hide it from view. – PhiNotPi – 2012-02-17T23:25:04.060

@PhiNotPi Only 2 out of my three tests this evening, though I think minitech may have done you a favour with his two latest entries. It was a lot more open earlier in the week. – Gareth – 2012-02-17T23:25:26.740

I have decided that deleting my bot was a stupid thing to do, so I have restored it. – PhiNotPi – 2012-02-18T00:15:52.483

Finally remembered to fix that bug, so please recompile Dirichlet. – Peter Taylor – 2012-02-18T07:18:32.567

Is it possible to run an extended test run? Right now, all of the top-scoring bots only have two wins each. This means that it is possible that this is just due to chance, and not because they are actually the best bots that are expected to win the final. I am not asking for a full 100-round test, just something a little longer than the current 10-round test. – PhiNotPi – 2012-02-19T18:31:47.477

@PhiNotPi I've added a 50 round test run. Your bot won 3 such test runs in a row. Others were close to you, but different ones each time (bob, christophe and schwarzenbeck). – Gareth – 2012-02-19T21:36:38.627

Did you use the current versions for each bot? – PhiNotPi – 2012-02-19T21:38:57.390

@PhiNotPi No, sorry. I didn't spot that you'd updated all yours, and now there's a new C++ one too. I'll update and re-run. – Gareth – 2012-02-19T21:42:50.320

@PhiNotPi Ok, all bots are up to date now. You obliterated the opposition on my first 10 round run by winning 6 times. I've posted my third 10 round test run and another 50 round run. – Gareth – 2012-02-19T22:04:03.203

The deadline is so close, I can't stand the anticipation! – PhiNotPi – 2012-02-20T22:32:12.973

Even though I won, my guilty conscious is making it feel like the metagaming I did was cheating, or at least unfair. – PhiNotPi – 2012-02-21T01:16:41.227

1@PhiNotPi: Don't feel guilty. You won within the rules. – Steven Rumbalski – 2012-02-21T17:03:29.350

Answers

9

Perl

I tried a little harder this time. It's a really simple complex strategy, but I've set up the framework for expansion.

Edit: Complete redo. This thing is in it for the win.

    sub prob{
$_[0]+$_[1]-$_[0]*$_[1]
}

$_=<>;
INPUT:{

tr/ /,/;
@in = eval;
for(1..$#in){
 $bids[$rnum][$in[$_]]++
}
for(0..$#in){
 $tbids[$rnum][$in[$_]]++
}
$rnum++;
$_=<>;
if($_ ne"\n"){redo INPUT}
}

for(0..100){$pre[$_]=0}

dirichlet: for(2..$#in/2+2){    #rough approximation, 
$pre[$_]=prob($pre[$_], 1/int($#in/2+1))
}

CDP:{
    @cdps1=(1,1,1,2,2,3,3,4);
    @cdps2=(-2,-1,0,1,1,2,2,3,3);
    for($a=0;$a<8;$a++){
    for($b=0;$b<9;$b++){
     $sum=$cdps1[$a]+$cdps2[$b];
     if($sum<1){$sum=1};
     $pre[$sum] = prob($pre[$sum], 1/72);
    }
    }
}

blazer: {
for($r=1;$r<$rnum;$r++){
winner: for($pnt=1;$pnt<101;$pnt++){
        if($tbids[$r][$pnt] == 1){
            if($pnt > 2){
                $winnum[$pnt]++;
            $wins++;
            }
        last winner
        }
}
    }
    if($wins==0){
    $pre[3]=prob($pre[3], 1);last blazer
    }
    for(1..100){
    $pre[$_]=prob($pre[$_], $winnum[$_]/$wins);
    }
}

CC1: for($pnt=1;$pnt<101;$pnt++){
    if($tbids[$rnum-1][$pnt] == 1){
        $pre[$pnt] = prob($pre[$pnt], 1);last CC1
    }
    if($pnt==100){
        for($pnt2=1;$pnt2<100;$pnt2++){
        $pre[$pnt2] = prob($pre[$pnt2], $tbids[$rnum-1][$pnt2]/($#in+1));
    }
    }
}

CC2: for($pnt=1;$pnt<101;$pnt++){
    if($rnum-2<0){$pre[7] = prob($pre[7], 1);last CC2}
    if($tbids[$rnum-2][$pnt] == 1){
        $pre[$pnt] = prob($pre[$pnt], 1);last CC2
    }
    if($pnt==100){
        $pre[7] = prob($pre[7], 1);last CC2
    }
}

one: {
$pre[1] = prob($pre[1], 1);
}

two: {
$pre[2] = prob($pre[2], 1);
}

five: {
$pre[5] = prob($pre[5], 1);
}

eight: {
$pre[8] = prob($pre[8], 1);
}

fortytwo: {
$pre[42] = prob($pre[42], 1);
}

mueller: {
    $a=($#in+2)/4;
    $pre[int$a]=prob($pre[int$a], 1)
}

schwarzenbeck: {
    $a=($#in+2)/4+1;
    $pre[int$a]=prob($pre[int$a], 1)
}

beckenbauer: {
    $a=($#in+2)/4+2;
    $pre[int$a]=prob($pre[int$a], 1)
}

totalbots: {
    $pre[$#in+1]=prob($pre[$#in+1], 1)
}

joe: {
$sum=0;
    for(1..100){
    $sum+=$tbids[$rnum-1][$_];
}
    $average=$sum/($#in+1);
    if($average==0){$average=10};
    $pre[$average]=prob($pre[$average], 1);
}

node: {
$max=0;$maxloc=0;
for(1..100){
    if($tbids[$rnum-1][$_]>$max){$max=$tbids[$rnum-1][$_];$maxloc=$_}
}
$maxloc--;
#if($maxloc==0){
$maxloc=20;
#}
if($rnum==1){$maxloc=3}
    $pre[$maxloc]=prob($pre[$maxloc], 1);
}
#print"\n@pre\n\n";

decide: for(1..100){if($pre[$_]<0.5){print; last decide}}

This program takes input one line at a time, followed by two newlines:

perl PhiNotPi2.plx
1 2 3 3 2
2 1 3 1 3
2 1 1 1 3
[empty line]

PhiNotPi

Posted 2012-02-04T01:00:53.370

Reputation: 26 739

Ok, this takes metagaming to extremes. – Peter Taylor – 2012-02-19T23:19:33.197

@petertaylor Am I going too far out of line? Should I revert to my original? – PhiNotPi – 2012-02-20T00:59:07.220

2This is a site notorious for rules lawyers - it's perfectly fair. But I am coming to the conclusion that the stack exchange mechanism might not be the best one for king-of-the-hill competitions. – Peter Taylor – 2012-02-20T08:27:39.767

I have also come to that conclusion. We need to create a method of hiding the bots from view in future competitions. For all I know, someone is metagaming against my bots right now. – PhiNotPi – 2012-02-20T12:48:19.770

Lol, this was my idea :P. Since you have already implemented, and I'm feeling lazy, I'll let you have it :) Note that the only kind of entry that this cannot easily beat are ones that implement randomness – mellamokb – 2012-02-20T16:16:40.837

8

Chef

Since always betting 1 is now a losing strategy, the obvious thing to do is to always bet 2 instead. So let me do that. To make this otherwise boring entry a little more interesting, I decided to write it in Chef:

Shirred Eggs.

This recipe prints the number 2 and, in doing so, yields two delicious
shirred eggs.

Ingredients.
2 eggs

Cooking time: 12 minutes.

Pre-heat oven to 175 degrees Celsius.

Method.
Put eggs into mixing bowl. Pour contents of the mixing bowl into the
baking dish. Shirr the eggs. Bake the eggs until shirred.

Serves 1.

As a bonus, the program actually more or less works as a real — if trivial — recipe, even if it does kind of read as if the writer was a bit, um, baked. The Chef grammar seems to make it pretty hard to write anything that involves something more complicated than mixing stuff in a bowl and baking it and still have it work both as a program and as a recipe, especially if any of the verbs one wants to use are even slightly irregular (like "fry" → "fried").

Edit: Changed the recipe from fried to shirred eggs — thanks to Blazer for the suggestion! The cooking time and temperature should be considered advisory only; I haven't actually tried the recipe yet myself, so I can't vouch for their accuracy.

Ilmari Karonen

Posted 2012-02-04T01:00:53.370

Reputation: 19 513

I think this outputs 1: see my comment at http://codegolf.stackexchange.com/a/4851.

– msh210 – 2012-02-06T19:05:50.117

It outputs 2, at least using the Acme::Chef interpreter. The final loop is there just for obfuscation, and so that the diners won't have to eat the eggs raw.

– Ilmari Karonen – 2012-02-06T19:17:48.293

Ah, right, I missed the fact that the eggs were already in the baking dish and that that's not what's decremented. – msh210 – 2012-02-06T19:21:06.667

2You call it shirred eggs, which is actually done in a baking dish and that would make the recipe an actual valid cooking recipe and grammatically correct. shirr the eggs. shirr the eggs until shirred. horray for having culinary education under my belt! :) – Blazer – 2012-02-08T04:06:28.147

or rather, Shirr the eggs. Bake the eggs until shirred – Blazer – 2012-02-08T21:30:03.167

1the cooking time/temperature seems about right :). of course, always use those as guidelines only, as it is the chef who determines whether something is done or not, not time/temperature itself! – Blazer – 2012-02-08T23:34:08.207

4

Python (Blazer)

This bot analyzes the previous rounds and records the numbers that win. Winning numbers that appear more often will therefore have a better chance of getting picked. It will then randomly choose numbers from winning numbers (other than 1 or 2). it will choose 2 3 instead if it is the first round.

Input is read one line at a time. simply enter an empty line to stop accepting input

A trick is to just paste (it automatically accepts each line with \n within the paste) and hit enter twice

You may now just runt the script with a filename in the command line:

python bidding.py bidding.txt

file should look like this:

10 4 12 11 12 4 7 3 3
1 2 9 15 1 15 15 9 3
3 21 6 4 3 8 6 13 1

-

import random
import sys

winning = [] # record the winning numbers

content = sys.argv[1].split('\n')  
for each in content:
    x = map(int, each.split())
    if len(x)+sum(x) == 0: 
        continue 

    y = []
    for each in x:
        if x.count(each) == 1:
            y.append(each)
    if len(y) > 0: 
        if min(y) not in [1,2]:  #never choose 1 or 2
            winning.append(min(y))

# choose an output
if len(winning) == 0:
    print 3
else:
    print random.choice(winning)

edit: added or sum(rounds) == 0 to compensate for the recent first-round-all-zeros change

edit: problems in comments fixed, also made it able to receive input from a filename, and never chooses '2' anymore since competition has weeded that out as well. works with either all-0's as the starting input or no data in the file at all

edit2: forgot a min()

edit3: changed input to suit question's input needs

Blazer

Posted 2012-02-04T01:00:53.370

Reputation: 1 902

Causes a small problem with the scorer - I have to press enter to get the score for each round. Not much of a problem for my 10 round test runs, but could be a pain for the 100 round run. – Gareth – 2012-02-06T23:00:24.820

@Gareth, wrap it in a bash script. echo "$@" | python bidding.py should do the job. – Peter Taylor – 2012-02-06T23:33:13.123

I've tried this as Peter suggested, but I'm getting an error TypeError: unsupported operand type(s) for +: 'int' and 'list' for line 23. I'm using Python 2.6.1, is that the problem? do I need a newer version? I get the same problem without using the bash script. – Gareth – 2012-02-07T20:30:35.047

@Gareth would it help if I made it so input is piped from sys.argv[1] with a filename? – Blazer – 2012-02-08T21:31:04.320

@Blazer I'm not sure that that's the problem. I'm calling the program from the command line myself using your example invocation and getting the error I gave above. Is there something in there that's incompatible with Python 2.6.1? – Gareth – 2012-02-08T21:40:01.827

@gareth I fixed it, it should work now, and you don't need to write a bash script for it to work anymore now, just input a filename with the content in it – Blazer – 2012-02-08T22:23:30.037

The scorer program gives the input itself as an argument as shown Input section of the question. Not sure how I can get it to give it as a file. – Gareth – 2012-02-08T22:45:55.987

I dont know how to get my command line to accept multiline arguments when you call the script for me to test it myself – Blazer – 2012-02-08T23:00:39.413

Never mind, I've made a small alteration to this program and to Joe - the content line in both now reads content = sys.argv[1].split('\n') and I've commented out the 2 lines that refer to file. This makes it all work fine. Let me know if you want me to undo that alteration in any way. – Gareth – 2012-02-08T23:01:52.363

if that alteration makes it work for you, then that's fine by me! I just dont know why I can't do multiline arguments like: python bidding.py "1 2 3 1 2\n4 5 6 4 5" – Blazer – 2012-02-08T23:05:02.823

@Blazer: Because the shell is not C and " does not enclose C strings. However if you are using bash, you should be able to type python bidding.py '1 2 3 1 2 (note: no closing quote yet!), then press enter, and then continue typing 4 5 6 4 5'. – celtschk – 2012-02-21T07:52:28.630

4

Python (2.6)

Extremely simple, but still I'm curious how it will perform compared to the other approaches.

import sys, random
try:
    s = sys.stdin.readlines()[-2]
    m = min(int(x) for x in s.split())
except IndexError:
    m = random.choice([1,1,1,2,2,3,3,4])
a = random.choice([-2,-1,0,1,1,2,2,3,3])
print max(m + a, 1)

Just pipe in the bids via stdin, e.g. python testbid.py < bids.txt.

EDIT: changed for the 'first round all zeros'

EDIT: changed the 'magic numbers' a bit (a second time)

ChristopheD

Posted 2012-02-04T01:00:53.370

Reputation: 1 599

1shouldn't m = random.choice(1,2,2,3,3,3) be m = random.choice([1,2,2,3,3,3])? – Blazer – 2012-02-07T06:37:40.193

It threw an error where Blazer said it might. I've put in the square brackets for the test run and it appears to have worked. – Gareth – 2012-02-07T21:19:10.960

@Blazer: yes, absolutely (small typo on my part). Thanks for notifying. – ChristopheD – 2012-02-07T23:28:04.420

3

Schwarzenbeck (Scala)

object Schwarzenbeck extends App {
  println ((args(0).split('\n')(0).split(' ').length+1)/4+1)
}

Schwarzenbeck isn't supposed to score the goals. He is the cleanup for Beckenbauer, which follows soon. :)

To use it, you need a compiler and compile it

scalac Schwarzenbeck.scala 

Then you can run it:

scala Schwarzenbeck 'your ar-
gu-
ment' 

Edit: Further adjustments.

user unknown

Posted 2012-02-04T01:00:53.370

Reputation: 4 210

1Given that Schwarzenbeck was not supposed to score the goals, I'd say it utterly failed :-) – celtschk – 2012-02-21T08:05:04.713

Yes, I had a dilemma: I made a line of 3 players, and expected Müller to score the most points, but from a strategic position, Schwarzenbeck marked the ultimate defense line. The football metapher failed, since my defense line scored the goals. :) – user unknown – 2012-02-21T08:28:38.163

3

Strategist (Ruby )

Implements hundreds of simple strategies: for each round, picks the one which would have won the most previous rounds:

require 'Matrix'
def winner guesses
  g=guesses.sort
  while g[0]&&g[0]==g[1]
    g.shift while g[0]==g[1]
    g.shift
  end
  g[0]
end

def prob g
  prob=[0]*100;best=0;n=g.size*(g[0].size-1)
  g.each{|r|r[1..-1].each{|v|prob[v-1]+=1.0/n}};
  prob.map!{|v|v/n}
end    

def regression x, y, degree
  return y if x.size==1 
  x_data = x.map {|xi| (0..degree).map{|pow| (xi**pow.to_f) }}
  mx = Matrix[*x_data]
  my = Matrix.column_vector y
  begin
    r = ((mx.t * mx).inv * mx.t * my).transpose.to_a[0]
  rescue Exception => e
    r=[0]*degree;r[-1]=y[-1].to_f/(x[-1]**degree)
  end
  r
end

brains=((1..50).map{|w|[proc{|g|w},
    proc{|g|best=0;(p=prob g).each_with_index{|v,i|
      best=i if(v+i/100.0/w)<p[best]};best+1}]}+
  (1..7).map{|w|[proc{|g|p=1; if (g[1]) then h=g[1..-1];x=(1..h.size).to_a
      p=0;regression(x,h.map{|r|winner r},w).each_with_index{|v,i|
      p+=v*(g.size**i)};end;p.to_i},
    proc{|g|b=g[0].size/4;if g[1] then pred=[];h=g[1..-1]
      x=(1..h.size).to_a;h[0].size.times{|i|p=0
      regression(x,h.map{|r|r[i]},w).each_with_index{|v,i|p+=v*((x[-1]+1)**i)}
      pred<<[[p.to_i,1].max,100].min}
      (1..100).each{|i|if !pred.include?(i) then b=i;break;end};end;b}]}+
  (-1..1).map{|w|[proc{|g|r=g[0].size; if g.size>1 then
      f=g[1..-1].flatten;r=(f.inject{|s,v|s+v}/f.size.to_f+w).to_i;end;r},
    proc{|g|r=g[0].size/2; if g.size>1 then
      r=(g[1..-1].inject(0){|s,v|s+winner(v)}/(g.size.to_f-1)+w).to_i;end;r},
    proc{|g|(winner(g[-1])||9)+w}  ]}+
  [proc{|g|b=0;(p=prob g).each_with_index{|v,i|b=i if v<p[b]};b+1}]).flatten

games = ARGV[0].split("\n").map{|l|l.split.map{|v|v.to_i}}
winpct=[0]*brains.size
(games.size-1).times{|round|
  entries=games[round+1].dup
  brains.each_with_index{|b,i|
    entries[0]=pick=[b[games[0..round]],1].max
    winpct[i]+= 1.0/games.size if winner(entries)==pick 
  }
}
best=0;
winpct.each_index{|i|best = i if (winpct[i]>winpct[best])}
puts brains[best][games]

I'm not sure I've got the input format right - I'm not sure how to generate multi-line command line arguments for testing it on windows. (This method seems to work on IDEone.)

AShelly

Posted 2012-02-04T01:00:53.370

Reputation: 4 281

I can't test it right now, I'm at work and won't be home until after 9.30 (GMT). Does this SO question help with the multi-line arguments?

– Gareth – 2012-02-20T17:16:25.400

Just tested this and it's giving me an error - strategist.rb:48:in 'each': No such file or directory - 42 2 6 10 8 6 5 7 6 1 5 8 3 6 3 4 26 2 10 1 26 8 42 5 3 7 (Errno::ENOENT). I'll stop considering new entries after 11PM, but I'll delay the scoring run slightly to give you time to look at the bug if you want. – Gareth – 2012-02-20T22:02:47.987

Okay, I think the problem was that you had ARGF instead of ARGV. After making that change the program guesses 1 every time. Any ideas what I can do to fix it? – Gareth – 2012-02-20T22:40:26.240

Can you add this line to the top and tell me what it prints when giving it the 2nd round input (2 lines of data): p ARGV.map{|l|l};exit (None of the SO answers to the question you reference or similar ones seem to give me the expected input) – AShelly – 2012-02-20T23:21:21.593

It prints out ["1 2\n3 4\n5 6\n1 2"] for the test input in the question. – Gareth – 2012-02-20T23:25:42.760

Thanks. I think this will do it: games = ARGV.split("\n").map{|l|l.split.map{|v|v.to_i}} – AShelly – 2012-02-20T23:29:00.383

I had to add a '[0]' to the ARGV. Now it gives an answer for the first line then strategist.rb:33:in 'block (2 levels) in <main>': unexpected return (LocalJumpError) – Gareth – 2012-02-20T23:34:43.283

updated - not sure why I wasn't getting this error before. – AShelly – 2012-02-20T23:47:53.243

2

Perl

I figured that I might as well enter the inevitable. More serious entries coming soon. As a bonus, this entry will never lose in one-on-one competition.

print 1

PhiNotPi

Posted 2012-02-04T01:00:53.370

Reputation: 26 739

not win every competition. in a one-on-one with another onesconfident, it will tie – Blazer – 2012-02-05T22:28:52.000

No! I can't believe I forgot about that case! I'll fix that. – PhiNotPi – 2012-02-05T22:34:43.270

One of my conclusions when I started designing my entry is that each bot should submit 1 at least 1/n of the time, to do their fair share in preventing onesconfident from walking away with it. – Peter Taylor – 2012-02-06T08:43:39.257

@Peter: Don't worry, I took care of that. :)

– Ilmari Karonen – 2012-02-06T10:51:31.357

2

JavaScript (node.js)

Counts what was most popular last round and bids one less than that, wrapping to 20 and bidding 3 on the first round.

var lastRound = /[^\n]+$/.exec(process.argv[2]);
var numbers = {};
var re = /\d+/g;
var match;

while(match = re.exec(lastRound)) {
    numbers[match] = numbers[match] >>> 0 + 1;
}

var maxKey = -1;

for(var i in numbers) {
    if(maxKey === -1 || numbers[i] > numbers[maxKey]) {
        maxKey = i;
    }
}

if(maxKey == 0) {
    // First round. Bid 3.
    console.log(3);
} else if(maxKey == 1) {
    // Bid 20.
    console.log(20);
} else {
    // Bid one less.
    console.log(maxKey - 1);
}

How to invoke:

node script.js 'the argument'

Ry-

Posted 2012-02-04T01:00:53.370

Reputation: 5 283

Looking at the results of the most recent test run, this doesn't behave as documented. Any idea why not? – Peter Taylor – 2012-02-07T22:32:58.967

1@PeterTaylor I wonder if it's the first for loop? Should if(i in numbers) be if(matches[i] in numbers) do you think? – Gareth – 2012-02-07T22:50:47.650

@minitech After a little poking around at it, it looks like the regex is only matching the one number of the input - don't know enough about javascript or Nodejs to be able to say why though. Also, do you need to split the input on newlines to get the last round? – Gareth – 2012-02-07T23:38:06.793

@Gareth: Indeed it is. Updated, though if it performed better originally then I don't mind :) – Ry- – 2012-02-07T23:53:44.710

Unfortunately it's throwing an error for every round except the first now: node.js:201 throw e; // process.nextTick error, or 'error' event on first tick TypeError: Cannot read property 'length' of null at Object.<anonymous> (minitech1.js:6:23) – Gareth – 2012-02-08T19:40:26.370

@Gareth: Sorry, yeah, didn't have time to test the new one (on a school computer). I'll delete & fix. – Ry- – 2012-02-08T19:42:05.830

@Gareth: It's fixed! :) – Ry- – 2012-02-08T19:49:54.057

2

Perl (Bob)

$_=<>;
INPUT:{

tr/ /,/;
@in = eval;
for(1..$#in){
 $bids[$rnum][$in[$_]]++
}
for(0..$#in){
 $tbids[$rnum][$in[$_]]++
}
$rnum++;
$_=<>;
if($_ ne"\n"){redo INPUT}
}

for(0..100){$pre[$_]=0}

blazer: {
for($r=1;$r<$rnum;$r++){
winner: for($pnt=1;$pnt<101;$pnt++){
        if($tbids[$r][$pnt] == 1){
            if($pnt > 2){
                $winnum[$pnt]++;
            $wins++;
            }
        last winner
        }
}
    }
    if($wins==0){
    $pre[3]++;last blazer
    }
}

CC1: for($pnt=1;$pnt<101;$pnt++){
    if($tbids[$rnum-1][$pnt] == 1){
        $pre[$pnt]++;last CC1
    }
}

CC2: for($pnt=1;$pnt<101;$pnt++){
    if($rnum-2<0){$pre[7]++;last CC2}
    if($tbids[$rnum-2][$pnt] == 1){
        $pre[$pnt]++;last CC2
    }
    if($pnt==100){
    $pre[7]++;last CC2
    }
}

one: {
$pre[1]+=2;
}

two: {
$pre[2]+=2;
}

five: {
$pre[5]+=2;
}

eight: {
$pre[8]+=2;
}

fortytwo: {
$pre[42]++;
}

mueller: {
    $a=($#in+2)/4;
    $pre[int$a]++;
}

schwarzenbeck: {
    $a=($#in+2)/4+1;
    $pre[int$a]++;
}

beckenbauer: {
    $a=($#in+2)/4+2;
    $pre[int$a]++;
}

totalbots: {
    $pre[$#in+1]++;
}

joe: {
$sum=0;
    for(1..100){
    $sum+=$_*$tbids[$rnum-1][$_];
}
    $average=$sum/($#in+1);
    if($average==0){$average=10};
    $pre[$average]++;
}

node: {
$max=0;$maxloc=0;
for(1..100){
    if($tbids[$rnum-1][$_]>$max){$max=$tbids[$rnum-1][$_];$maxloc=$_}
}
$maxloc--;
#if($maxloc==0){
$maxloc=20;
#}
if($rnum==1){$maxloc=3}
    $pre[$maxloc]++;
}
choice: for(1..100){
    if($pre[$_]==1){ 
$count++;
    if($count==3){print; last choice}
}
    if($_==100){print"98"}
}

See "Bob" for how to invoke.

PhiNotPi

Posted 2012-02-04T01:00:53.370

Reputation: 26 739

That's a very recursive guide to invocation ;-) – Gareth – 2012-02-08T22:08:41.817

There's actually a chain of logic put into place: Alice describes how she takes input. Eve mentions that she takes input the same as Alice. Eve also mentions that she takes input the same as Bob. Thus, Bob takes the same input format as Alice, which is described. – PhiNotPi – 2012-02-08T22:33:33.717

2

Perl (Alice)

$_=<>;
INPUT:{

tr/ /,/;
@in = eval;
for(1..$#in){
 $bids[$rnum][$in[$_]]++
}
for(0..$#in){
 $tbids[$rnum][$in[$_]]++
}
$rnum++;
$_=<>;
if($_ ne"\n"){redo INPUT}
}

for(0..100){$pre[$_]=0}

blazer: {
for($r=1;$r<$rnum;$r++){
winner: for($pnt=1;$pnt<101;$pnt++){
        if($tbids[$r][$pnt] == 1){
            if($pnt > 2){
                $winnum[$pnt]++;
            $wins++;
            }
        last winner
        }
}
    }
    if($wins==0){
    $pre[3]++;last blazer
    }
}

CC1: for($pnt=1;$pnt<101;$pnt++){
    if($tbids[$rnum-1][$pnt] == 1){
        $pre[$pnt]++;last CC1
    }
}

CC2: for($pnt=1;$pnt<101;$pnt++){
    if($rnum-2<0){$pre[7]++;last CC2}
    if($tbids[$rnum-2][$pnt] == 1){
        $pre[$pnt]++;last CC2
    }
    if($pnt==100){
    $pre[7]++;last CC2
    }
}

one: {
$pre[1]+=2;
}

two: {
$pre[2]+=2;
}

five: {
$pre[5]+=2;
}

eight: {
$pre[8]+=2;
}

fortytwo: {
$pre[42]++;
}

mueller: {
    $a=($#in+2)/4;
    $pre[int$a]++;
}

schwarzenbeck: {
    $a=($#in+2)/4+1;
    $pre[int$a]++;
}

beckenbauer: {
    $a=($#in+2)/4+2;
    $pre[int$a]++;
}

totalbots: {
    $pre[$#in+1]++;
}

joe: {
$sum=0;
    for(1..100){
    $sum+=$_*$tbids[$rnum-1][$_];
}
    $average=$sum/($#in+1);
    if($average==0){$average=10};
    $pre[$average]++;
}

node: {
$max=0;$maxloc=0;
for(1..100){
    if($tbids[$rnum-1][$_]>$max){$max=$tbids[$rnum-1][$_];$maxloc=$_}
}
$maxloc--;
#if($maxloc==0){
$maxloc=20;
#}
if($rnum==1){$maxloc=3}
    $pre[$maxloc]++;
}
choice: for(1..100){
    if($pre[$_]==1){ 
$count++;
    if($count==2){print; last choice}
}
    if($_==100){print"99"}
}

Takes input similar to my other bots.

perl Alice.plx
1 4 3 12
3 2 4 11
[blank line]

PhiNotPi

Posted 2012-02-04T01:00:53.370

Reputation: 26 739

2

Perl (Eve)

I completely redid this entry to help pave the way for my other bots.

$_=<>;
INPUT:{

tr/ /,/;
@in = eval;
for(1..$#in){
 $bids[$rnum][$in[$_]]++
}
for(0..$#in){
 $tbids[$rnum][$in[$_]]++
}
$rnum++;
$_=<>;
if($_ ne"\n"){redo INPUT}
}

for(0..100){$pre[$_]=0}

blazer: {
for($r=1;$r<$rnum;$r++){
winner: for($pnt=1;$pnt<101;$pnt++){
        if($tbids[$r][$pnt] == 1){
            if($pnt > 2){
                $winnum[$pnt]++;
            $wins++;
            }
        last winner
        }
}
    }
    if($wins==0){
    $pre[3]++;last blazer
    }
}

CC1: for($pnt=1;$pnt<101;$pnt++){
    if($tbids[$rnum-1][$pnt] == 1){
        $pre[$pnt]++;last CC1
    }
}

CC2: for($pnt=1;$pnt<101;$pnt++){
    if($rnum-2<0){$pre[7]++;last CC2}
    if($tbids[$rnum-2][$pnt] == 1){
        $pre[$pnt]++;last CC2
    }
    if($pnt==100){
    $pre[7]++;last CC2
    }
}

one: {
$pre[1]+=2;
}

two: {
$pre[2]+=2;
}

five: {
$pre[5]+=2;
}

eight: {
$pre[8]+=2;
}

fortytwo: {
$pre[42]++;
}

mueller: {
    $a=($#in+2)/4;
    $pre[int$a]++;
}

schwarzenbeck: {
    $a=($#in+2)/4+1;
    $pre[int$a]++;
}

beckenbauer: {
    $a=($#in+2)/4+2;
    $pre[int$a]++;
}

totalbots: {
    $pre[$#in+1]++;
}

joe: {
$sum=0;
    for(1..100){
    $sum+=$_*$tbids[$rnum-1][$_];
}
    $average=$sum/($#in+1);
    if($average==0){$average=10};
    $pre[$average]++;
}

node: {
$max=0;$maxloc=0;
for(1..100){
    if($tbids[$rnum-1][$_]>$max){$max=$tbids[$rnum-1][$_];$maxloc=$_}
}
$maxloc--;
#if($maxloc==0){
$maxloc=20;
#}
if($rnum==1){$maxloc=3}
    $pre[$maxloc]++;
}
choice: for(1..100){
    if($pre[$_]==1){ 
$count++;
    if($count==1){print; last choice}
}
    if($_==100){print"100"}
}

Takes one input format: the same as "Bob" and "Alice".

PhiNotPi

Posted 2012-02-04T01:00:53.370

Reputation: 26 739

2

Python (Joe)

This is by no means designed to win, but I'm throwing it out there anyway to add some color to the crowd :) It bids the average of the last round (Average Joe). Invoked the same as my original answer (which I will now name because it seems like that's what all the cool kids are doing, and it helps to distinguish the two). if starting round, it bids 10.

content = sys.argv[1].split('\n')  
x = map(int, content[-1].split())
print sum(x)/len(x) if sum(x) != 0 else 10

edit: changed method of input to suit question's input method

Blazer

Posted 2012-02-04T01:00:53.370

Reputation: 1 902

2

Python (CopyCat)

Yet another, this time it copies the exact answer the last winner had, if there was one. It's dsigned to both try to win and block other bidders. bids 5 if first round, bids a random number from the previous round if there was somehow no winner

content = sys.argv[1].split('\n')
x = map(int, content[-1].split())
y = []
for each in x:
    if x.count(each) == 1:
        y.append(each)
print min(y) if sum(y) > 0 else random.choice(x) if sum(x) > 0 else 5

Blazer

Posted 2012-02-04T01:00:53.370

Reputation: 1 902

2

Python (TotalBots)

I think this one will be my last, but we'll see. It takes advantange of knowing how many bots there are by simply outputting the number of competing bots, so if there are 17 bots (current number of bots, plus this one), it will output 17

content = sys.argv[1].split('\n')
print len(content[-1].split())

Blazer

Posted 2012-02-04T01:00:53.370

Reputation: 1 902

2

Perl (Health Inspector)

print ((((((2)**(2))*((2)**(2)))/((((2)**(2))*(2))*(2)))+((((2)**(2))*(2))/((2)+((2)*(((((2)**(2))+((2)*(2)))+(((2)*(2))/((2)**(2))))**(((2)/(2))/(2)))))))+((((2)-(2))/((((2)**(2))+(2))*((2)+(2))))*(rand(2))))

I bet you can guess what it does.

PhiNotPi

Posted 2012-02-04T01:00:53.370

Reputation: 26 739

2

C++ (Educated Guess)

I already thought I would have missed the deadline, but thanks to the extension I can still add my entry. This program compiles with g++. The program tries to guess the statistics of the other entries, and to choose the smallest one not likely to be chosen by any other one.

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <deque>
#include <cstdlib>
#include <ctime>
#include <exception>
#include <stdexcept>

typedef std::vector<int> botvec;
typedef std::vector<botvec> scorevec;

// read all the scores from the given string
// note that this only does minimal error checking
// the result is a vector of vector, each entry of which
// represents one round. That is, the vectors in the vector
// correspond to the lines of the command line argument.
scorevec read_past_scores(char const* scoretext)
{
  scorevec past_scores;

  std::istringstream is(scoretext);
  std::string line;

  scorevec::size_type size = 0;

  while (std::getline(is, line))
  {
    past_scores.push_back(botvec());

    std::istringstream ils(line);
    int i;
    while (ils >> i)
      past_scores.back().push_back(i);
    if (size == 0)
      size = past_scores.back().size();
    else if (past_scores.back().size() != size)
      throw std::runtime_error("invalid score format");
  }
  return past_scores;
}

struct counts { int count[100]; };
struct prob { double p[100]; };

int generate_answer(scorevec& past_scores)
{
  int const number_of_players = past_scores.front().size();
  if (past_scores.front().front() == 0) // initial round
    past_scores.pop_back();

  // Pre-fill the counts to get reasonable probabilities also for
  // insufficient statistics (and the statistics *will* be
  // insufficient!). Bias in favour of small numbers.
  counts initial;
  for (int i = 0; i < 100; ++i)
    initial.count[i] =
      i < number_of_players? 100*(number_of_players-i) : 1;

  std::deque<counts> playercounts(number_of_players, initial);

  // add the actual guesses (with a high factor, to give them high
  // weight against the initial counts)
  for (int i = 0; i < past_scores.size(); ++i)
    for (int j = 0; j < number_of_players; ++j)
      playercounts[j].count[past_scores[i][j]-1]+=5000;

  // drop the own guesses
  playercounts.pop_front();

  // calculate the probabilities corresponding to the counts
  std::vector<prob> playerprobabilities(playercounts.size());
  for (int i = 0; i < playercounts.size(); ++i)
  {
    double sum = 0;
    for (int k = 0; k < 100; ++k)
      sum += playercounts[i].count[k];
    for (int k = 0; k < 100; ++k)
      playerprobabilities[i].p[k] = playercounts[i].count[k]/sum;
  }

  // for each selection, estimate the expected number of other players
  // who will bet on it. Return the first one with an expectation
  // below 1.5.
  for (int i = 0; i < 100; ++i)
  {
    double estimate = 0;
    for (int j = 0; j < number_of_players; ++j)
      estimate += playerprobabilities[j].p[i];
    if (estimate < 1.5)
      return i+1;
  }

  // in the unlikely case that such a choice doesn't exist (meaning
  // there are far more than 100 players), just return 100.
  return 100;
}

int main(int argc, char* argv[])
{
  if (argc < 2)
  {
    std::cerr << "Missing score argument!\n";
    return EXIT_FAILURE;
  }

  try
  {
    scorevec past_scores = read_past_scores(argv[1]);

    std::srand(std::time(0));

    std::cout << generate_answer(past_scores) << std::endl;

    return EXIT_SUCCESS;
  }
  catch(std::exception& e)
  {
    std::cerr << e.what() << "\n";
    return EXIT_FAILURE;
  }
  catch(...)
  {
    std::cerr << "Unknown error\n";
    return EXIT_FAILURE;
  }
}

celtschk

Posted 2012-02-04T01:00:53.370

Reputation: 4 650

1

dirichlet.c

#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>

main(int argc, char* argv[])
{
    int handle;
    char *str;
    int32_t bits, val, n = 0;

    if (argc) {
        for (str = argv[1]; *str; str++)
            if (*str == 32) n++;
            else if (*str == 10) break;
    }

    n /= 2;
    if (n > 99) n = 99;

    handle = open("/dev/urandom", O_RDONLY);
    do {
        read(handle, &bits, sizeof bits);
        bits &= 0x7fffffff;
        val = bits % n;
    } while (bits - val + (n-1) < 0);
    close(handle);

    printf("%d", 2 + val);
}

I think this goes through random bits too fast to use /dev/random, however much I'd prefer to. If anyone wants to test it on Windows you'll have to port it yourself, because I don't have access to a Windows box with a C compiler.

Rationale

I didn't want to explain the logic behind this before the tournament was over, but now that the winner has been announced, I think it's time.

By the pigeon-hole principle (aka Dirichlet's principle, hence the name of the bot), if there are N competing bots then there is a number w in [1..1+N/2] which either won or would have won if selected. I therefore conclude that the optimal strategy will not select numbers greater than 1+N/2. But if N is even, selecting 1+N/2 creates a smaller winning slot. Therefore the slots which are worth selecting are [1..(N+1)/2].

That leaves the question of how to select a slot. For small numbers of bots I verified that there's a Nash equilibrium when each bot selects uniformly among the candidates, and I strongly suspect that this will continue to hold true.

The minor deviation in this bot's strategy from the theoretical one is simply metagaming.

Peter Taylor

Posted 2012-02-04T01:00:53.370

Reputation: 41 901

1

Brainfuck

To quote from the challenge:

"You can enter as many bots as you wish, so if anyone does enter a bot that only guesses 1, you can enter another that does the same to render it useless."

Well, since PhiNotPi did enter one, let me enter another. Just to be different, I'll do it in Brainfuck:

+++[->++++<]>[-<++++>]<+.

Of course, now that betting 1 is no longer a feasible strategy, the obvious thing to do now is to bet 2 instead...

Edit: Split answer into two per comments, rewrote both programs in more interesting languages.

Ilmari Karonen

Posted 2012-02-04T01:00:53.370

Reputation: 19 513

Firstly, one entry per answer please. Secondly, I know that someone could post 100 answers each printing one of the digits from 1 to 100 in order to guarantee not losing - equally someone else could do exactly the same meaning that no-one will win. In a game of football (soccer) all 11 players could stand on the goal line to guarantee that the other team doesn't score. It never normally happens that way because it would be much of a game if they did would it? – Gareth – 2012-02-06T11:11:05.280

Thirdly, this objection should really have been raised in the sandbox - after all that is its purpose.

– Gareth – 2012-02-06T11:11:20.413

@Gareth: OK, I've split the answer into two. As for the reasonability of the entries, you yourself suggested that, if anyone were to submit "onesconfident", someone else could do the same to counter it. At that point, of course, submitting "twosconfident" makes just as much sense as submitting "onesconfident" did in the first place, so... – Ilmari Karonen – 2012-02-06T18:25:25.400

Ps. No, I'm not planning on submitting "threesconfident"; I've made my point, and I have no particular interest in continuing this arms race, even if someone else does decide to escalate it further. – Ilmari Karonen – 2012-02-06T19:39:48.937

1The neat thing about this is that now I cannot delete my onesconfident entry without allowing this entry to win. – PhiNotPi – 2012-02-06T21:29:03.607

I just compiled a brainfuck compiler that I downloaded from here and ran this program. The output was !, I assume I'm missing something, or have buggered up somewhere?

– Gareth – 2012-02-06T23:15:51.567

@PhiNotPi, if the other entries are minimally aggressive then on average there should be one of them which picks 1 each turn, so onesconfident should lose anyway. – Peter Taylor – 2012-02-06T23:34:38.457

I'm not sure why, but adding an extra + at the beginning made the program work as it should. Maybe the brainfuck implementation I have is buggy. – Gareth – 2012-02-07T00:19:25.413

@Gareth: That interpreter is buggy -- it doesn't execute the first char in the program. (If you insist on using that interpreter, you can work around the bug by prepending a non-functional character, such as a space, to the program.) – Ilmari Karonen – 2012-02-07T00:25:45.763

@IlmariKaronen That explains it then. – Gareth – 2012-02-07T00:27:37.757

1@Peter: Why do you think so? Given that both mine and PhiNotPi's programs are in the race, there's no reason for anyone else to submit a program that ever bets 1 (if they want that program to win, that is). – Ilmari Karonen – 2012-02-07T00:36:07.703

@IlmariKaronen, I think this exposes a downside of the open implementation of king-of-the-hill competitions. You get a circle of changes to adjust to the field rather than an analytic approach. – Peter Taylor – 2012-02-07T09:40:40.983

1

Beckenbauer (Scala)

object Beckenbauer extends App {
  println ((args(0).split('\n')(0).split(' ').length+1)/4+2)
}

With the help of Schwarzenbeck, Beckenbauer is supposed to score some goals. Without Schwarzenbeck, he's nothing.

Details about running and compilation: See [Schwarzenbeck][1]

Edit: Playing deeper in the room now, too.

user unknown

Posted 2012-02-04T01:00:53.370

Reputation: 4 210

1

Mueller (Scala)

object Mueller extends App {
  println ((args(0).split('\n')(0).split(' ').length+1)/4)
}

If you know Schwarzenbeck and Beckenbauer, you surely expected Mueller. Here he is. He will benefit much from Beckenbauer and Schwarzenbeck and is supposed to win.

Details about running and compilation: See Schwarzenbeck

Closer to the goal, now.

user unknown

Posted 2012-02-04T01:00:53.370

Reputation: 4 210

1

Batch Scripting

echo 5

My submission, gives 5 as its answer every time ;-)

mellamokb

Posted 2012-02-04T01:00:53.370

Reputation: 5 544

1

Eight.bat

echo 8

Another simple answer, gives 8 every time.

mellamokb

Posted 2012-02-04T01:00:53.370

Reputation: 5 544

1

FivesCancel (PHP)

Cancels mellamokb's "always 5" solution.

5

Ry-

Posted 2012-02-04T01:00:53.370

Reputation: 5 283

1

EightsCancel (PHP)

Cancels mellamokb's "always 8" solution. Sorry, mellamokb!

8

Ry-

Posted 2012-02-04T01:00:53.370

Reputation: 5 283

Here we go again, competition :P – mellamokb – 2012-02-17T13:59:56.823

1

Python 2.7 - Copycat2

Copies the second last round's winner. Oh no! otherwise outputs 7.

import sys
content = sys.argv[1].split('\n')
x = map(int, content[-2].split()) if len(content) > 1 else [7]
y = []
for each in x:
    if x.count(each) == 1:
        y.append(each)
print min(y) if sum(y) > 0 else random.choice(x) if sum(x) > 0 else 7

Blazer

Posted 2012-02-04T01:00:53.370

Reputation: 1 902

1

Shell script (Deep Thought)

OK, so that I get a slight second chance, here's another entry, this time a shell script (should work with any shell). This always gives the answer to the question of life, the universe and everything.

echo 42

Actually this algorithm is not entirely correct because I omitted the 7.5 million year delay. :-)

celtschk

Posted 2012-02-04T01:00:53.370

Reputation: 4 650

Too late for tonight's test run sorry, but I'll do another in the morning. – Gareth – 2012-02-19T22:08:47.600