Caveman Duels (or: Me poke you with sharp stick)

148

52

Caveman mad. Other caveman take stick but stick was for me. Caveman fight!


Description

Caveman need sharp stick to stab other caveman. Other caveman also try to stab with sharp stick. Caveman can sharpen stick, poke with stick, or block poky sticks.

If caveman poke other caveman with sharp stick, other caveman run away and me victory. But if other caveman smartly blocking when me poking, nothing happen except my stick become blunt and me need to sharpen again.

Caveman lazy. Also, caveman dumb. Caveman no know what to do, so caveman need fancy techno computer program to tell caveman what to do.

Input

Your program's input will be a history of the events that have happened, where S stands for sharpen (i.e. the caveman sharpened his stick), P stands for poke, and B stands for block. The input will be a history of both sides (you and the opponent), so your and the opponent's moves will be separated with a comma (,).

Example input:

SPB,SBB

This means that the player sharpened his/her stick, then poked, then blocked, and the opponent sharpened, then blocked, then blocked again.

You will receive no input on turn 1.

Output

The output is very similar to the input (because the caveman is not very smart). Your program should output S to sharpen, P for poke, and B for block. Only the first character of output will be taken into account, and any other input will be treated as a B (block) command.

  • S: sharpen

    When sharpening, the caveman's stick's sharpness goes up by 1 and the stick gets 1 extra poke. Each poke reduces the stick's sharpness by 1, and if the stick's sharpness is 0, it's too dull to poke with. Sharpness starts at 0. If sharpness gets to 5, the stick is a sword! (See below.)

    If the opponent pokes while you are sharpening (and they have a sharpness > 0), the opponent wins!

  • P: poke

    When poking, the caveman's stick's sharpness goes down by 1 and you poke your opponent! If your opponent is sharpening, you win! If the opponent is poking, your stick hits your opponent's stick and they both get duller (by 1 "sharpness unit"). If the opponent is blocking, nothing happens except that your stick becomes duller.

    If you poke when your stick's sharpness is 5 or greater, your stick becomes a sword and you always win! (Unless your opponent also has a sword and also chose P; in that case, they both become duller, and may revert to sticks if their sharpness falls below 5.)

    You cannot poke with a sharpness of 0. If you do, nothing will happen.

  • B: block

    When you block, nothing happens when your opponent pokes. If your opponent is not poking, block does nothing.

    Blocking does not protect against a sword, even if you also have one!

Rules and constraints

Additional rules are:

  • Your program can read and write files in its own folder (no stealing!) if you want to save data, but you can't access anything outside of it (and cavemen don't have internet connection out in the wilderness).
    • Important note on files: If you save files, remember to save them in the directory players/YourBotsName/somefile.foo! The current working directory for your program will not be your program's!
  • Cavemen are fair: One program can not have code specific for another program, and programs can not help each other. (You may have multiple programs, but they can't interact with each other in any way.)
  • The caveman judge is not patient. If the cavemen take more than 100 turns each to decide a winner, the judge gets bored and both cavemen lose.

If your program breaks a rule or doesn't follow the specification, the program is disqualified, removed from playerlist.txt, and all duels restart from the beginning. If your program is disqualified, the caveman leader (me!) will comment on your program's post and explain why. If you aren't breaking any rules, your program will be added to the leaderboard. (If your program is not on the leaderboard, there is no explanatory comment on your post, and you posted your program before the "Last updated" time below, tell the caveman leader! Maybe he forgot it.)

In your post, please include:

  • A name.
  • A shell command to run your program (ex. java MyBot.java, ruby MyBot.rb, python3 MyBot.py, etc.).
    • Note: input will be appended to this as a command line argument.
    • The cavemen use Ubuntu 14.04, so make sure your code works (freely) on it.
  • A version number, if your code works differently on different versions of your chosen language.
  • Your code (obviously).
  • How to compile the code, if necessary.

Controller code / testing, example bot

The caveman leader wrote the control code in C++, and posted it on a Github repo. You can run and test your program there.

A very, very simple program (1 line!) is also posted in the answers below.

Scoring and leaderboard

Scoring is easy. Whichever caveman wins gets a point. The caveman with the most points after 3 duels against every other caveman becomes the new caveman leader!

150     Watson
147     SpeculativeSylwester
146     Gruntt
141     BashMagnon
126     ChargerMan
125     PrisonRules
124     ViceLeader
122     MultiMarkov
122     CaveDoctor
120     RegExMan
120     Hodor
117     FancyTechnoAlgorithm
116     Semipatient
113     Watcher
108     BobCaves
105     MinimaxMan
104     Oracle
102     MaybeMarkov
97      Nash
95      Sicillian
95      Feint
95      Basilisk
94      SharpMan
93      Darwin
91      Nigel
91      JavaMan
88      Entertainer
88      CarefulBot
85      CaveMonkey
84      SSBBP
82      SirPokealot
79      MasterPoker
77      Unpredictable
76      IllogicalCaveman
75      SharpenBlockPoke
75      HuddleWolfWithStick
72      WoodenShield
68      PokeBackBot
68      PatientBlacksmith
66      PatientWolf
58      MonteCarloMan
58      BlindFury
56      BinaryCaveman
55      PokeBot
55      CavekidBlocks
53      Swordmaster
53      Blocker
52      NakedEarlyNerd
52      ModestCaveman
50      LatePokeBot
40      Trickster
39      SwordLover
38      ForeignCaveman
36      Swordsmith *
28      Touche
27      WantASword
27      FoolMeOnce
24      PeriodicalCavemanCicada
11      Aichmophobic

(this leaderboard was auto-magically generated)

Players marked with a * threw some kind of error or exception at some point; these players also have a comment on their posts.

Players who could not be included in the tests for any reason (these players will have a comment on their posts explaining the problem): Monkey, Elephant, FacileFibonacci, StudiousSylwester.

Last updated: Aug 3 00:15 (UTC).

Doorknob

Posted 2014-07-23T14:10:56.457

Reputation: 49 044

Does the program for matches automatically quit when one caveman loses? Or do we handle this in our caveman's code? – ASCIIThenANSI – 2015-04-09T15:06:10.637

2Is this still open? I see people adding new submissions, but I don't see the leaderboard being updated. – ASCIIThenANSI – 2015-04-10T16:36:09.543

I'm surprised no one seems to have attempted to find the minimax strategy yet. It seems like the obvious thing to do. – user2357112 – 2014-07-24T15:47:38.330

@user2357112 I don't think minimax is an improvement here. I mean, you could design a minimax implementation, but since the logic is so simple, the same exact behavior can be expressed with a finite state machine. (i.e. the bot will never sharpen until the opponent is dull because if it does, the minimizing move of the opponent will be to poke and you lose, the bot will always block until we have a sword because the maximizing move for our bot will always be to block, etc.) – HuddleWolf – 2014-07-24T18:32:18.197

My friends and I usually play this game with hand-signalled "guns" instead... – ikdc – 2014-07-24T22:17:28.150

@HuddleWolf: I don't mean the minimax algorithm. That would require sequential decisions, and this is not a game of sequential decisions. I mean the minimax strategy; the set of move probabilities that maximizes the minimum expected score over all possible opponent strategies. – user2357112 – 2014-07-25T01:10:01.387

if I were to train a markov chain generator on the output of all of the existing entries playing against each other, and put the resulting training data into my entry, would that break the "cavemen are fair" rule? – Sparr – 2014-07-25T03:29:49.987

@Sparr Yes, "One program can not have code specific for another program". (In fact, that code would be specific for every single other program!) – Doorknob – 2014-07-25T10:52:54.740

@Doorknob is there some degree of fuzziness that would make it acceptable? That is, if the chain length was restricted to, say, 5 moves, then no part of the lookup table would be specific to a single other program. That is, the best way to respond to SPBSS as a single datum isn't aiming at a particular other program, but would involve data on that behavior by every other program. – Sparr – 2014-07-25T22:30:08.043

@Doorknob: What about training against a composite opponent whose strategy for each move is equivalent to picking one of the existing opponents and asking it to choose the move? If that's barred, I don't think any further optimizations I can make are within the rules. I've written a minimax bot, so literally any deviation from the minimax strategy that improves its score would be optimizing for the specific spread of opponents. Any attempt to write new bots would be doing by hand the training the machine learning algorithm would do.

– user2357112 – 2014-07-26T04:28:54.103

@Sparr I'm just going to go with "use your best judgement" - If you could write the same program with no knowledge of the current bots except their general strategy, go ahead. But don't look through the code here to find the most optimal 3rd move, for example. – Doorknob – 2014-07-26T04:33:27.093

@Lennart_96 No need to comment here; I sort the answers by "active" so I can see when submissions are added/edited. – Doorknob – 2014-07-26T04:34:27.987

3Many of the entries seem to allow for negative sharpness in their calculations. The rules as written say that nothing happens when you poke with zero sharpness. Does that "nothing" also mean your sharpness stays zero, instead of being decremented? – Sparr – 2014-07-26T14:24:37.573

also, the judge seems to treat a zero sharpness poke as a block. clarification, @Doorknob ? – Sparr – 2014-07-26T15:13:22.910

@Sparr In text above: You cannot poke with a sharpness of 0. If you do, nothing will happen. and it is interpreted as block since that is the default behaviour should you bot spit out any other char. Even the history given to the programs reflects that. – Sylwester – 2014-07-26T21:21:15.960

@Doorknob When you start CavemanDuels, do you remove the files made in previous iterations? – Sylwester – 2014-07-26T21:54:48.187

@Sylwester ahh, that last part is what I was missing, the history being altered. That fixes the problem with sharpness calculation that I thought some bots had. – Sparr – 2014-07-27T03:01:04.707

@Sylwester No, because that would require some sort of complex which-files-to-remove algorithm. That's left up to the submissions themselves. – Doorknob – 2014-07-27T11:53:51.570

@Doorknob It's hardly complex since you have used git. I'm doing git clean -f players before I run your program. – Sylwester – 2014-07-27T14:07:04.517

I'm with Sylwester... Adding logic to my bot to check the file creation time of files and see if they are older than the current contest seems unnecessarily complex. Then again, maybe having access to data about the last few thousand contests is worthwhile... – Sparr – 2014-07-27T19:04:21.443

@Sylwester That requires keeping the Git repo up to date, which is not always possible if you want to test your bot against a specific other one, for example. – Doorknob – 2014-07-27T23:25:59.110

@Sparr Or, you could just check if there are no arguments (i.e. it's the first round) and delete all your files if so. – Doorknob – 2014-07-27T23:26:21.090

@Doorknob I'm talking about the beginning of a new run of the whole tournament. The RPSLV contest specifies that data will be cleared at that time. In this contest, there's no reliable way to do it. On one hand, I'm considering looking at file timestamps to get rid of data more than a few minutes old. On the other hand, the solution I just submitted is going to eventually take advantage of being able to track data between tournaments, which will make it smarter in the long run. – Sparr – 2014-07-28T00:00:40.597

Doorknob: I'm not sure about your answer to @Sparr. I can see in my log that two challenges against mine sometimes are alternating so you cannot be sure if your previous session has ended even if one starts. – Sylwester – 2014-07-28T00:07:56.817

The judge running multiple threads will play havoc with bots that try to track data, it seems? – Sparr – 2014-07-28T00:11:21.200

6

This needs to be here: http://dresdencodak.com/comics/2009-09-22-caveman_science_fiction.jpg
Maybe it will get the imagination going. :)

– Evi1M4chine – 2014-07-28T01:10:26.653

just an FYI on the data directories from the RPSLV tourney. Don't have any submissions yet that do data files, but when they come in, I'm just going to auto purge all the /Player/[Name]/Data directories at the beginning of each tourney – Eoin Campbell – 2014-07-28T09:48:17.673

Hello, I was surprised by my score, I know my bash script was not that smart. While I was trying to remove weak spots, I discovered this: If you read the out.txt, BashMagnon didn't win a round against CaveDoctor but if I make a simulation I get SBSBSBSBSP,SBBBBBSBSP each time, which is "sword against stick" and should win. Could you double check? – Emmanuel – 2014-07-30T08:50:46.210

@Emmanuel How did you run this simulation? The way that it works is that on round 1, both programs get no input, then on round two, they both get S,S, etc. – Doorknob – 2014-07-30T09:18:14.283

@Doorknob a=./players/BashMagnon/BashMagnon.sh, b="./players/CaveDoctor/CaveDoctor.lua", S="" then I repeated that command at each step : S=${S%%,*}$($a $S),${S##*,}$(lua $b $S); echo $S – Emmanuel – 2014-07-30T09:31:01.627

@Emmanuel I just tried here a lot of times and CaveDoctor always win against BashMagnon with SBBBBBP,SBSBSBS. Are you sure after the 5B CaveDoctor doesnt output a P ? – wendelbsilva – 2014-07-30T16:52:36.323

It was my fault I used a wrong command which was not inverting the right and left sequences. S=${S%%,*}$($a $S),${S##*,}$(lua $b ${S##*,},${S%%,*}); echo $S is the right command – Emmanuel – 2014-07-30T18:30:40.453

Answers

33

Darwin - C

Who needs strategy, anyway? Have a group of cavemen go at each other and let natural selection do the rest!


We use a very simple model for out caveman's primitive brain: it has no memory and only takes the sharpness of his and his opponent's stick into account. Those are used as the variables for a binary polynomial of some finite order. Each action (block, sharpen and poke) has an associated polynomial whose result determines the relative probability of choosing this action. That's pretty much all there is to it---start with some random coefficients and optimize iteratively.

The bot:

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

/* magic numbers */
#define SWORD_SHARPNESS 5
#define PROGRAM_DIM 4 /* polynomial order + 1 */
#define DEFAULT_FILENAME "players/Darwin/program"

typedef double real;
typedef real program[PROGRAM_DIM][PROGRAM_DIM];
typedef program caveman_brain[3];

typedef char action; /* S, B or P */
/* encodes a pair of actions */
#define ACTION_PAIR(a1, a2) (((int)(a1) << (sizeof(action) * 8)) | (a2))

real eval_program(const program p, double x, double y) {
    real v = 0;
    int i, j;

    for (i = 0; i < PROGRAM_DIM; ++i) {
        real w = 0;
        for (j = 0; j < PROGRAM_DIM; ++j)
            w = x * w + p[i][j];
        v = y * v + w;
    }

    if (v < 0)
        v = 0;
    return v;
}
void read_program(FILE* f, program p) {
    int i, j;
    for (i = 0; i < PROGRAM_DIM; ++i) {
        for (j = 0; j < PROGRAM_DIM; ++j) {
            double v;
            fscanf(f, "%lg", &v);
            p[i][j] = v;
        }
    }
}

int blunt(int* s) {
    int temp = *s;
    if (temp)
        --*s;
    return temp;
}
void sharpen(int* s) { ++*s; }
/* takes two sharpness/action pairs and updates the sharpness accordingly.
 * returns negative value if first caveman wins, positive value if second
 * caveman wins and 0 otherwise. */
int act(int* s1, action a1, int* s2, action a2) {
    switch (ACTION_PAIR(a1, a2)) {
        case ACTION_PAIR('B', 'B'): return 0;
        case ACTION_PAIR('B', 'S'): sharpen(s2); return 0;
        case ACTION_PAIR('B', 'P'): return blunt(s2) >= SWORD_SHARPNESS ? 1 :
                                                                          0;
        case ACTION_PAIR('S', 'B'): sharpen(s1); return 0;
        case ACTION_PAIR('S', 'S'): sharpen(s1); sharpen(s2); return 0;
        case ACTION_PAIR('S', 'P'): sharpen(s1); return *s2 > 0 ? 1 : 0;
        case ACTION_PAIR('P', 'B'): return blunt(s1) >= SWORD_SHARPNESS ? -1 :
                                                                          0;
        case ACTION_PAIR('P', 'S'): sharpen(s2); return *s1 > 0 ? -1 : 0;
        case ACTION_PAIR('P', 'P'): {
            int t1 = blunt(s1), t2 = blunt(s2);
            if (t1 >= SWORD_SHARPNESS && t2 < SWORD_SHARPNESS)
                return -1;
            else if (t2 >= SWORD_SHARPNESS && t1 < SWORD_SHARPNESS)
                return 1;
            else
                return 0;
        }
    }
}
/* processes a pair of strings of actions */
int str_act(int* s1, const char* a1, int* s2, const char* a2) {
    for (; *a1 && *a2; ++a1, ++a2) {
        int winner = act(s1, *a1, s2, *a2);
        if (winner)
            return winner;
    }
    return 0;
}

double frandom() { return (double)rand() / RAND_MAX; }

/* chooses an action based on self and opponent's sharpness */
action choose_action(const caveman_brain b, int s1, int s2) {
    double v[3];
    double sum = 0;
    double r;
    int i;
    for (i = 0; i < 3; ++i) {
        v[i] = eval_program(b[i], s1, s2);
        sum += v[i];
    }
    r = frandom() * sum;
    if (r <= v[0])
        return 'B';
    else if (r <= v[0] + v[1])
        return 'S';
    else
        return 'P';
}

/* portable tick-count for random seed */
#ifdef _WIN32
#include <Windows.h>
unsigned int tick_count() { return GetTickCount(); }
#else
#include <sys/time.h>
unsigned int tick_count() {
    struct timeval t;
    gettimeofday(&t, NULL);
    return 1000 * t.tv_sec + t.tv_usec / 1000;
}
#endif

int main(int argc, const char* argv[]) {
    const char* filename = DEFAULT_FILENAME;
    const char *a1, *a2;
    FILE* f;
    caveman_brain b;
    int s1 = 0, s2 = 0;
    int i;

    srand(tick_count()); rand();

    a1 = argc > 1 ? argv[1] : "";
    if (*a1) {
        a2 = strchr(a1, ',');
        if (a2 == NULL) {
            printf("invalid input!\n");
            return 1;
        }
        ++a2;
    } else
        a2 = a1;

    if (argc > 2)
        filename = argv[2];

    f = fopen(filename, "r");
    if (f == NULL) {
        printf("failed to open `%s'\n", filename);
        return 1;
    }
    for (i = 0; i < 3; ++i)
        read_program(f, b[i]);
    fclose(f);

    str_act(&s1, a1, &s2, a2);
    printf("%c\n", choose_action(b, s1, s2));

    return 0;
}

Compile with: gcc darwin.c -odarwin -w -O3. Run with: ./darwin <history>.

The bot reads the coefficients from a file named program in the players/Darwin directory (a different file can be specified as a second command-line argument). This program seems to do well:

0.286736 0.381578 -0.128122 1.33933 
0.723126 0.380574 1.21659 -0.9734 
0.924371 0.998632 -0.0951554 0.744323 
-0.113888 -0.321772 -0.260496 -0.136341 

0.280292 -0.699782 -0.246245 1.27435 
-1.24563 -0.959822 -0.745656 0.0347998 
-0.917928 -0.384105 0.319008 -0.70434 
0.484375 0.802138 0.0967234 0.638466 

0.406679 0.597322 1.39409 0.902353 
-0.735946 0.742589 0.955567 0.643268 
-0.503946 0.446167 1.002 0.328205 
0.26037 0.113346 0.0517265 -0.223298 

Save as players/Darwin/program.

Following is a program that generates program files that can be used by the bot (doesn't have to be compiled if you use the program file above):

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

/* magic numbers */
#define SWORD_SHARPNESS 5
#define MAX_TURN_COUNT 100
#define PROGRAM_DIM 4 /* polynomial order + 1 */
#define CAVEMAN_COUNT 500
#define GENERATION_COUNT 12
#define DUEL_COUNT 8
#define ERROR_BACKOFF 0.5
#define DEFAULT_FILENAME "players/Darwin/program"

typedef double real;
typedef real program[PROGRAM_DIM][PROGRAM_DIM];
typedef program caveman_brain[3];

typedef char action; /* S, B or P */
/* encodes a pair of actions */
#define ACTION_PAIR(a1, a2) (((int)(a1) << (sizeof(action) * 8)) | (a2))

real eval_program(const program p, double x, double y) {
    real v = 0;
    int i, j;

    for (i = 0; i < PROGRAM_DIM; ++i) {
        real w = 0;
        for (j = 0; j < PROGRAM_DIM; ++j)
            w = x * w + p[i][j];
        v = y * v + w;
    }

    if (v < 0)
        v = 0;
    return v;
}
void write_program(FILE* f, const program p) {
    int i, j;
    for (i = 0; i < PROGRAM_DIM; ++i) {
        for (j = 0; j < PROGRAM_DIM; ++j)
            fprintf(f, "%g ", p[i][j]);
        fprintf(f, "\n");
    }
    fprintf(f, "\n");
}

int blunt(int* s) {
    int temp = *s;
    if (temp)
        --*s;
    return temp;
}
void sharpen(int* s) { ++*s; }
/* takes two sharpness/action pairs and updates the sharpness accordingly.
 * returns negative value if first caveman wins, positive value if second
 * caveman wins and 0 otherwise. */
int act(int* s1, action a1, int* s2, action a2) {
    switch (ACTION_PAIR(a1, a2)) {
        case ACTION_PAIR('B', 'B'): return 0;
        case ACTION_PAIR('B', 'S'): sharpen(s2); return 0;
        case ACTION_PAIR('B', 'P'): return blunt(s2) >= SWORD_SHARPNESS ? 1 :
                                                                          0;
        case ACTION_PAIR('S', 'B'): sharpen(s1); return 0;
        case ACTION_PAIR('S', 'S'): sharpen(s1); sharpen(s2); return 0;
        case ACTION_PAIR('S', 'P'): sharpen(s1); return *s2 > 0 ? 1 : 0;
        case ACTION_PAIR('P', 'B'): return blunt(s1) >= SWORD_SHARPNESS ? -1 :
                                                                          0;
        case ACTION_PAIR('P', 'S'): sharpen(s2); return *s1 > 0 ? -1 : 0;
        case ACTION_PAIR('P', 'P'): {
            int t1 = blunt(s1), t2 = blunt(s2);
            if (t1 >= SWORD_SHARPNESS && t2 < SWORD_SHARPNESS)
                return -1;
            else if (t2 >= SWORD_SHARPNESS && t1 < SWORD_SHARPNESS)
                return 1;
            else
                return 0;
        }
    }
}
/* processes a pair of strings of actions */
int str_act(int* s1, const char* a1, int* s2, const char* a2) {
    for (; *a1 && *a2; ++a1, ++a2) {
        int winner = act(s1, *a1, s2, *a2);
        if (winner)
            return winner;
    }
    return 0;
}

double frandom() { return (double)rand() / RAND_MAX; }
double firandom() { return 2.0 * rand() / RAND_MAX - 1.0; }

/* chooses an action based on self and opponent's sharpness */
action choose_action(const caveman_brain b, int s1, int s2) {
    double v[3];
    double sum = 0;
    double r;
    int i;
    for (i = 0; i < 3; ++i) {
        v[i] = eval_program(b[i], s1, s2);
        sum += v[i];
    }
    r = frandom() * sum;
    if (r <= v[0])
        return 'B';
    else if (r <= v[0] + v[1])
        return 'S';
    else
        return 'P';
}

typedef struct {
    caveman_brain brain;
    int sharpness;
    int score;
} caveman;
void init_caveman(caveman* c, const caveman* m, double e) {
    int p, i, j;
    c->score = 0;
    for (p = 0; p < 3; ++p) {
        for (i = 0; i < PROGRAM_DIM; ++i) {
            for (j = 0; j < PROGRAM_DIM; ++j) {
                c->brain[p][i][j] = m->brain[p][i][j] + firandom() * e;
            }
        }
    }
}
int duel(caveman* c1, caveman* c2) {
    int winner;
    int turn;
    c1->sharpness = c2->sharpness = 0;
    for (turn = 0; turn < MAX_TURN_COUNT; ++turn) {
        winner = act(&c1->sharpness,
                     choose_action(c1->brain, c1->sharpness, c2->sharpness),
                     &c2->sharpness,
                     choose_action(c2->brain, c2->sharpness, c1->sharpness));
        if (winner)
            break;
    }
    if (winner < 0)
        ++c1->score;
    else if (winner > 0)
        ++c2->score;
    return winner;
}

/* portable tick-count for random seed */
#ifdef _WIN32
#include <Windows.h>
unsigned int tick_count() { return GetTickCount(); }
#else
#include <sys/time.h>
unsigned int tick_count() {
    struct timeval t;
    gettimeofday(&t, NULL);
    return 1000 * t.tv_sec + t.tv_usec / 1000;
}
#endif

int main(int argc, const char* argv[]) {
    const char* filename = DEFAULT_FILENAME;
    FILE* f;
    caveman* cavemen;
    caveman winner;
    int gen;
    double err = 1.0;
    int i;

    srand(tick_count()); rand();
    memset(&winner, 0, sizeof(caveman));

    if ((cavemen = (caveman*)malloc(sizeof(caveman) * CAVEMAN_COUNT)) == NULL) {
        printf("not enough memory!\n");
        return 1;
    }

    for (gen = 0; gen < GENERATION_COUNT; ++gen) {
        int i, j, k;
        const caveman* leader;

        printf("[Gen. %d / %d] ", gen + 1, GENERATION_COUNT);
        fflush(stdout);

        for (i = 0; i < CAVEMAN_COUNT; ++i)
            init_caveman(&cavemen[i], &winner, err);

        for (i = 0; i < CAVEMAN_COUNT; ++i) {
            for (j = i + 1; j < CAVEMAN_COUNT; ++j) {
                for (k = 0; k < DUEL_COUNT; ++k)
                    duel(&cavemen[i], &cavemen[j]);
            }
        }

        leader = cavemen;
        for (i = 1; i < CAVEMAN_COUNT; ++i) {
            if (cavemen[i].score > leader->score)
                leader = &cavemen[i];
        }

        printf("Caveman #%d wins with %d victories in %d duels\n",
               leader - cavemen + 1,
               leader->score, (CAVEMAN_COUNT - 1) * DUEL_COUNT);

        memcpy(&winner, leader, sizeof(caveman));
        err *= ERROR_BACKOFF;
    }

    free(cavemen);

    if (argc > 1)
        filename = argv[1];
    printf("Dumping brain to `%s'\n", filename);
    f = fopen(filename, "w");
    if (f == NULL) {
        printf("failed to open `%s'\n", filename);
        return 1;
    }
    for (i = 0; i < 3; ++i)
        write_program(f, winner.brain[i]);
    fclose(f);

    return 0;
}

Compile with: gcc genprog.c -ogenprog -w -O3. Run with: ./genprog [output-filename].


Watson

What's the DNA of a winning caveman? Perhaps this fella has the answer:

# That's the actual logic. Initialization goes below.
def run():
    if his_sharpness[-10] - turn / 15 + 1 + turn % 3 - his_sharpness[-6] < 0:
        act(B=0, S=0, P=100) # 7.21% chance
    elif his_sharpness[-6] + 1 - his_sharpness[-2] < 0:
        act(B=0, S=0, P=100) # 4.15% chance
    elif his_history[-3] - my_history[-1] <= 0 and my_sharpness[-1] - turn / 10 <= 0:
        act(B=0, S=100, P=0) # 11.34% chance
    elif his_sharpness[-1] == 0:
        act(B=0, S=100, P=0) # 27.84% chance
    else:
        act(B=100, S=0, P=0) # 49.46% chance

# Boring stuff go here...

import sys, random

# Actions
block, sharpen, poke, idle = range(4)

# Converts textual history to internal format
def convert_history(textual_history):
    return ["BSP".index(action) for action in textual_history]

# Calculates sharpness after performing an action sequence
def calculate_sharpness(history):
    return history.count(sharpen) - history.count(poke)

# Returns a list containing the sharpness at the end of each turn
def sharpness_history(history):
    return [calculate_sharpness(history[:i + 1]) for i in range(len(history))]

# Acts based on the probability distribution (B%, S%, P%)
def act(B, S, P):
    r = random.random() * 100
    print "BSP"[(r >= B) + (r >= B + S)]

# Setup data
textual_history = sys.argv[1] if len(sys.argv) > 1 else ","
my_history, his_history = (convert_history(h) for h in textual_history.split(','))
my_sharpness, his_sharpness = (sharpness_history(h) for h in (my_history, his_history))
turn = len(my_history)
my_history, his_history = ([idle] * 16 + h for h in (my_history, his_history))
my_sharpness, his_sharpness = ([0] * 16 + s for s in (my_sharpness, his_sharpness))

# Make a move
run()

Run with: python Watson.py

Watson is the product of a genetic algorithm. Unlike Darwin, the genetic datum this time is an actual program, written in a tiny domain-specific language (here translated to Python).


Simple Sequence Beats Big Players

This little fella does surprisingly (or, maybe, not so surprisingly) well, especially against the leaders:

import sys
print "Simple Sequence Beats Big Players".split(' ')[
    len(sys.argv[1]) / 2 % 5 if len(sys.argv) > 1 else 0
]

Run with: python SSBBP.py

DarwinBot

Posted 2014-07-23T14:10:56.457

Reputation: 681

How do I compile and run this? Also, as mentioned in the question, you may only read/write files in the players/Darwin directory. – Doorknob – 2014-07-24T02:45:36.213

@Doorknob: fixed. – DarwinBot – 2014-07-24T06:26:00.103

I am getting these compile errors when compiling this code. (I'm on Ubuntu 14.04.)

– Doorknob – 2014-07-24T06:48:28.820

@Doorknob: Fixed. Should work now. – DarwinBot – 2014-07-24T06:56:06.713

I'm now getting undefined reference to \fmax'. --Edit-- Never mind, I did indeed need-lm`. – Doorknob – 2014-07-24T06:59:36.037

Alright, this worked perfectly and is included in the next leaderboard! – Doorknob – 2014-07-24T07:05:06.310

@Doorknob: Oh well, got rid of it anyway. Apparently it's C99 and some poor souls might not have it. No need to recompile though; the semantics haven't changed. – DarwinBot – 2014-07-24T07:12:39.453

Congratulations; Watson is the King of the Hill! – Doorknob – 2014-08-03T03:32:04.360

Thanks for arranging such a fun competition, Doorknob, and props to everyone else! There were some very clever, original and entertaining submissions. – DarwinBot – 2014-08-03T11:23:15.010

49

Unpredictable Caveman

me, he = (ARGV[0] || ' , ').split(',')

@possible_actions = %w[Sharpen Poke Block]

class String

  def sharpness
    @sharpness ||= count('S') - count('P')
  end

  def has_pointy_stick
    (1..4).cover? sharpness
  end

  def has_sword
    sharpness >= 5
  end

  def scary
    sharpness > 0
  end

end

def no action
  @possible_actions.delete(action)
end

def do!
  puts @possible_actions.sample[0]
end

no 'Block' if not he.has_pointy_stick

no 'Poke' if not me.scary

no 'Sharpen' if me.has_sword

no 'Block' if me.has_sword

do!

This caveman chooses randomly each round, but I've explained to him very simply that certain actions just don't make sense sometimes. Feel free to copy this code if you want to express different logic.

This is Ruby, save as 'unpredictable.rb' and run with ruby unpredictable.rb

histocrat

Posted 2014-07-23T14:10:56.457

Reputation: 18 102

46+1 for caveman language in your code. – Tim S. – 2014-07-23T19:57:13.183

Actually, I thing the no 'Block' should also be if my opponent has a sword. – njzk2 – 2014-07-25T14:18:41.130

The first no 'Block' actually covers that: a pointy stick is not a sword. – histocrat – 2014-07-25T14:43:35.317

2Why don't you use unless for the no 'Block' and no 'Poke' statements? (no 'Block' unless he.has_pointy_stick) – wchargin – 2014-08-09T22:17:38.167

24

Cave Doctor - Lua

"Me lose to new foreigners, knocked them out to study them"

When you've seen as many patients as the cave doctor, you begin to truly understand the cave man psyche (or so I hope). Cave doctor's game is pure strategy, he waits for pokes which he blocks in an attempt to disarm his opponent, but he won't let that opponent get close to making a sword. He tries to predict when it's safe to sharpen so he doesn't loose the upper hand.

caveman={havePointyStick=function (t)     
   local pointy=0   
   for i in t.stick:gmatch("[SP]") do
    if i=="S" then 
      pointy=pointy+1
    elseif pointy>0 then
      pointy=pointy-1
    end   
   end 
 t.sharp=pointy>0
 t.lastmove=t.stick:sub(t.stick:len())
 return pointy 
 end,
    Stupid=function (stick)--I put way to much effort in this...
      o = {} 
      setmetatable(o, caveman)
      o.smartness=0
      o.stick=stick
      caveman.__index = caveman
      return o
    end,
     Smart= function (stick)
      o ={} 
      setmetatable(o, caveman)
      o.smartness=100
      o.stick=stick
      caveman.__index = caveman
      return o
    end
       }


    if arg[1]==nil then  
       print("S")
    else   
      split=arg[1]:find(",")  
      me=caveman.Smart(arg[1]:sub(0,split-1)) 
      he=caveman.Stupid(arg[1]:sub(split+1)) 
      mesharp=me:havePointyStick()  
      hesharp=he:havePointyStick()
      if not he.sharp and mesharp<5 then print("S")--Go for the sword  
      elseif mesharp>4 or me.stick:len()>93 then
         if (mesharp>0) then print("P")--We're losing/about to win or time's running out
         else print("S")--uh-oh
         end
      else 
         u,g,h=he.stick:match("(B+)S+(B+)S+(B+)$")
         g,u,h=he.stick:match("(P+)S+(P+)S+(P+)$")
         if u~=nil and u==g and g==h then 
            if not me.sharp then print("S")
            else print("P")
            end
         elseif me.stick:match("SBSB$")~=nil then print("B")
         elseif he.stick:len()>7 and he.stick:match("P")==nil and me.lastmove~="S" then print("S")
         else
         b,u,h=he.stick:match("(B*)(S+)(B*)$")
         if u~=nil then
             if (h:len()>3 and me.lastmove=="B") or (b:len()>h:len() and b:len()>0 and h:len()>0) then print("S")
             else print("B")
             end
          else print("B")
          end   
      end   
   end 
end

Run with: lua CaveDoctor.lua

Nexus

Posted 2014-07-23T14:10:56.457

Reputation: 631

3This only lose twice in the current leaderboard? o.O – justhalf – 2014-07-25T08:25:39.507

Revision 5 throws a bunch of errors, so revision 4 is the one included in the current round of trials. – Doorknob – 2014-07-26T03:12:09.217

@Doorknob I think I fixed all of them, there was only one change to the actual logic anyways. – Nexus – 2014-07-26T04:24:04.450

18

ForeignCaveman

ForeignCaveman has no idea what you just said. He just... does stuff.

javac ForeignCaveman.java then java ForeignCaveman

public class ForeignCaveman {

    public static void main(String[] args) {
        int m = (int) (Math.random()*3);
        switch(m) {
            case 0: System.out.println('B'); 
                    break;
            case 1: System.out.println('P'); 
                    break;
            case 2: System.out.println('S'); 
                    break;
        }
   }
}

Kevin L

Posted 2014-07-23T14:10:56.457

Reputation: 1 211

9This probably has way too many upvotes for how bad it does – Kevin L – 2014-07-28T17:50:10.240

18

Vice-Leader

Doorknob♦ is leader. Me want be leader! Follow super intelligent program to become leader!

Compile: javac ViceLeader.java Run: java ViceLeader.

public class ViceLeader {

    public static void main(String[] args) {
        if (args.length == 0 || !args[0].contains(",")) {
            System.out.print("S");
            return;
        }
        String[] history = args[0].split(",");
        int mySharpness = getSharpness(history[0]);
        int enemySharpness = getSharpness(history[1]);

        // enough sharpness to strike until end of game
        if (100 - history[0].length() <= mySharpness) {
            System.out.print("P");
            return;
        }

        // sharpen while secure
        if (enemySharpness == 0) {
            System.out.print("S");
            return;
        }

        // enemy blocks the whole time and I didn't use this tactic on last turn
        if (isBlocker(history[1]) && history[0].charAt(history[0].length() - 1) != 'S') {
            System.out.print("S");
            return;
        }

        // TAKE HIM OUT!
        if (enemySharpness == 4 || mySharpness >= 5) {            
            System.out.print("P");
            return;
        }

        // enemy sharpens the whole time => sharpen to strike on next turn
        if (isSharpener(history[1])) {
            System.out.print("S");
            return;
        }

        System.out.print("B");
    }

    private static int getSharpness(String history) {
        int sharpness = 0;
        for (char move : history.toCharArray()) {
            if (move == 'S') {
                sharpness++;
            } else if ((move == 'P' && sharpness > 0) || move == '^') {
                sharpness--;
            }
        }
        return sharpness;
    }

    private static boolean isBlocker(String history) {
        if (history.length() < 3) {
            return false;
        }
        for (int i = history.length() - 1; i > history.length() - 3; i--) {
            if (history.charAt(i) != 'B') {
                return false;
            }
        }
        return true;
    }

    private static boolean isSharpener(String history) {
        if (history.length() < 3) {
            return false;
        }
        for (int i = history.length() - 1; i > history.length() - 3; i--) {
            if (history.charAt(i) != 'S') {
                return false;
            }
        }
        return true;
    }
}

Manu

Posted 2014-07-23T14:10:56.457

Reputation: 3 642

Why isn't this if (enemySharpness &lt;= 4 || mySharpness &gt;= 5) vs == ? – durron597 – 2014-07-30T19:52:53.777

@durron597 Because I only want to poke the enemy if he can make a sword on the next turn (which he will most likely do). VizeLeader doesn't poke often, it does it at the right time. – Manu – 2014-07-30T19:55:48.373

But you have a sword and your opponent doesn't... – durron597 – 2014-07-30T19:59:46.603

@durron597 No, it's an OR statement. It means "poke the opponent if I have a sword OR if he will soon have a sword". – Manu – 2014-07-30T20:01:14.700

7Oh goodness. Time to get another cup of coffee :) Or new contact lenses – durron597 – 2014-07-30T20:01:35.113

14

Maybe Markov 2.1

I think it uses Markov Chains to predict what the other caveman will do, but I only looked briefly at the wikipedia page about Markov Chains and decided it had too much text.

It tries to stay alive for 30 rounds and then builds up a table with current-next state changes, and reacts to what is thinks the other caveman will do.

The code contains a lot of unnecessary statements, but it performs pretty well.

EDIT

Detected a flaw in logic. Now it actually does something when it has a sword.

$ python3 players/MaybeMarkov/MaybeMarkov.py

import sys, itertools
from operator import itemgetter
from collections import defaultdict

SHARPEN, POKE, BLOCK, HALP = 'SPB?'

all_actions = SHARPEN, POKE, BLOCK
always = 1

def do(action):
    print(action)
    exit(0)

if len(sys.argv) < 2:
    do(SHARPEN)

class status:
    def __init__(self, actions):
        self.step = len(actions)
        self.last = actions[-1]
        self.sh = self.sharpness = actions.count(SHARPEN) - actions.count(POKE)
        self.dull = self.sharpness <= 0
        self.has_sword = self.sharpness >= 5
        self.actions = actions
        self.ratio = {act:actions.count(act)/self.step for act in all_actions}
        self.can_do = set(all_actions)

        if self.dull:
            self.can_do.remove(POKE)

    def can(self, action):
        return action in self.can_do


me, he = map(status, sys.argv[-1].split(','))
turns = me.step

if he.has_sword:
    if me.can(POKE)                :do(POKE)
    if always                      :do(SHARPEN)

if me.has_sword:
    if he.last != POKE and me.last == BLOCK :do(POKE)
    if he.can(POKE)                :do(BLOCK)
    if always                      :do(POKE)

if not he.can(POKE)                :do(SHARPEN)

if turns <= 4                      :do(BLOCK)
if turns < 30:
    if he.ratio[SHARPEN] == 1:
        if me.can(POKE)            :do(POKE)
        if always                  :do(SHARPEN)
    if always                      :do(BLOCK)

if turns > 97:
    do(POKE)

def react_on(action):
    do({
        HALP    : BLOCK,
        SHARPEN : POKE,
        POKE    : BLOCK,
        BLOCK   : SHARPEN
    }[action])

states = tuple(itertools.product(all_actions, all_actions))
change = defaultdict(lambda:defaultdict(lambda:0))
count  = defaultdict(lambda:0)

for i in range(1, turns):
    prev = me.actions[i-1], he.actions[i-1]
    now  = me.actions[i]  , he.actions[i]
    change[prev][now] += 1
    count[prev] += 1

current = change[me.last, he.last]
prediction = HALP

if len(current) is 0:
    do(BLOCK)

if len(current) is 1:
    if tuple(current.values())[0] > turns / 7:
        prediction = tuple(current.keys())[0][1]

counts = itemgetter(1)

if len(current) > 1:
    key1, value1 = max(current.items(), key=counts)
    current[key1] *= 0.9
    key2, value2 = max(current.items(), key=counts)
    if key1 == key2:
        prediction = key1[1]

react_on(prediction)

Lennart_96

Posted 2014-07-23T14:10:56.457

Reputation: 241

+1 for honesty. – wchargin – 2014-08-09T22:20:33.620

13

PeriodicalCicadaCaveman

This rather smart cave man has studied a certain Bug and realized no one can adjust their life style to take advantage of the prime number Cicada.

It hides / blocks for most of it's life, but occasionally pokes. Sure it's vulnerable to Swords, and spends a whole cycle with an unsharpened stick, but sharpening your stick when it's totally blunt? That's exactly what the others expect from it... not this Cicada

to compile: mcs program.cs to run mono program.exe

public class PeriodicalCicadaCaveman
{
  const int Periodic = 13; //Could be 17
  public static void Main(string[] args)
  {
    if (args.Length == 0) 
    {
          System.Console.WriteLine("S");
          return;
    }
    var arg1 = args[0];
    if(arg1.Length == 0) 
    {
        //Always start with a sharp stick
        System.Console.WriteLine("S");
        return;
    }
    var myHistory = arg1.Split(',')[0];
    var theirHistory = arg1.Split(',')[1];
    int sharpness = 0;
    int timeElapsed =  myHistory.Length;

    for(int i = 0; i < timeElapsed; i++)
    {
        if(myHistory[i] == 'S')  
        {
            sharpness++;
        }
        if(myHistory[i] == 'P')
        {
            sharpness--;
        }
    }

    if((myHistory.Length % 13) == 0 
            || timeElapsed > 90 // Running out of time! To hell with the routine
        )
    {
        //The Secada strikes!
        if(sharpness > 1)
        {
            System.Console.WriteLine("P");
            return;
        }
        else
        {
            System.Console.WriteLine("S"); 
            return;
        }
    }
    System.Console.WriteLine("B"); 

  }

}

Edit: Changed the sharpness-- code... if I poke either I win or my stick gets duller

Edit2: Added in Bobs suggestion

Edit: Changed to only poke when at sharpness 2, if the stick is ever at zero the other guy might make a sword.

Mikey Mouse

Posted 2014-07-23T14:10:56.457

Reputation: 291

I'm running on Ubuntu; will this compile under Mono? If so, how do I compile it and how do I run it? – Doorknob – 2014-07-23T22:19:33.550

To be honest, I don't know. I'm just about to head for bed. I can rewrite it to Java tomorrow morning at work. The Java code should be almost identical. – Mikey Mouse – 2014-07-23T22:42:49.373

5@Doorknob mcs program.cs will compile it, mono program will run it, but you'll need to replace the foo.Dump();s with System.Console.WriteLine(foo); (or add an extension method public static void Dump(this string value) { System.Console.WriteLine(value); }). – Bob – 2014-07-24T01:27:42.397

@Bob Thanks buddy, I've added in your extension method. – Mikey Mouse – 2014-07-24T08:37:52.117

Sorry, the actual default filename mcs generates is &lt;filename&gt;.exe, e.g. program.cs would become program.exe. So the run command would be mono program.exe. (I didn't have access to mono at the time of my earlier comment.) – Bob – 2014-07-24T09:32:59.040

I'm getting this compile error: error CS5001: Program \PeriodicalCicadaCaveman.exe' does not contain a static `Main' method suitable for an entry point` – Doorknob – 2014-07-25T07:39:21.077

@Doorknob Yeah, sorry, it's been ages since I've written Console Applications. It needed to be an array of strings, that's been fixed now – Mikey Mouse – 2014-07-25T08:29:39.823

Should be all fixed now, I've run it through a Visual Studio Console Project and passed some parameters to it – Mikey Mouse – 2014-07-25T13:09:42.750

This one doesn't work on the first turn, when there is no input. – Doorknob – 2014-07-26T03:06:42.980

@Doorknob Ok, one day I'll get this to work, hopefully before the final scores are in, should work now – Mikey Mouse – 2014-07-26T08:55:56.417

13

The Watcher

He watches his opponent's movements, always letting them show their hand before he strikes. He is particularly prepared for those who neglect to work toward a sword.

import sys, random

if len(sys.argv) > 1:
    history_self, history_other = sys.argv[1].split(',')
else:
    history_self = history_other = ""

def sharpness(history):
    ret = 0
    for action in history:
        if action == 'S':
            ret += 1
        elif action == 'P' and ret > 0:
            ret -= 1
    return ret

def weighted_random(dict):
    i = random.randrange(sum(dict.values()))
    for k, v in dict.items():
        i -= v
        if i < 0:
            return k

def action(history_self, history_other):
    sharpness_self = sharpness(history_self)
    sharpness_other = sharpness(history_other)
    if sharpness_self >= 5:
        return 'P'
    elif sharpness_other == 0:
        return 'S'  #Guaranteed safe
    elif sharpness_other == 1:
        #If the opponent isn't interested in a sword, time is on our side
        block_count = len(history_self) - len(history_self.rstrip('B'))
        if block_count > 3 and random.randrange(block_count) > 3:
            return 'S'
        else:
            return 'B'
    elif sharpness_other >= 5:
        return 'S'
    else:
        #Search for a weakness
        for i in range(10, 2, -1):
            if history_other[-i:] == history_other[-i*2:-i]:
                predicted_action = history_other[-i]
                if predicted_action == 'S':
                    if sharpness_self > 0:
                        return 'P'
                    else:
                        return 'S'
                elif predicted_action == 'B':
                    return 'S'
                elif predicted_action == 'P':
                    return 'B'
        #Presumably the opponent is random - respond with some educated randomness
        if sharpness_self == 0:
            return random.choice(['S','S','B'])
        return weighted_random({
            'S': sharpness_self,
            'B': 1,
            'P': sharpness_other,
        })

if __name__ == "__main__":
    print(action(history_self, history_other))

Filename: watcher.py

To run: python watcher.py

Basilisk

Seeks to destroy those who look at him too closely. Consistently beats the Watcher, but will probably fare worse overall.

import sys, random

if len(sys.argv) > 1:
    history_self, history_other = sys.argv[1].split(',')
else:
    history_self = history_other = ""

def sharpness(history):
    ret = 0
    for action in history:
        if action == 'S':
            ret += 1
        elif action == 'P' and ret > 0:
            ret -= 1
    return ret

def action(history_self, history_other):
    sharpness_self = sharpness(history_self)
    sharpness_other = sharpness(history_other)
    if sharpness_self >= 5:
        return 'P'
    elif len(history_self) < 13:
        return 'SBBSBPSBBSBPP'[len(history_self)]
    elif 5 + 5 * sharpness_self < random.randrange(len(history_self)):
        return 'S'
    elif sharpness_other == 0:
        if sharpness_self == 0 or random.randrange(sharpness_self) == 0:
            return 'S'
        else:
            return 'P'
    elif sharpness_other == sharpness_self:
        return 'P'
    else:
        return 'B'

if __name__ == "__main__":
    print(action(history_self, history_other))

Filename: basilisk.py

To run: python basilisk.py

Nash

Seeks to make his opponent's choices irrelevant, by choosing each move with a probability that accounts for its risks and rewards

import sys, random

if len(sys.argv) > 1:
    history_self, history_other = sys.argv[1].split(',')
else:
    history_self = history_other = ""

movemap = [ [(1.000000,0.000000),(0.473863,0.526137),(0.394636,0.605364),(0.490512,0.509488),(1.000000,0.000000)],
        [(0.695328,0.000000,0.304672),(0.275953,0.582347,0.141700),(0.192635,0.700391,0.106974),(0.196343,0.689662,0.113995),(0.289968,0.544619,0.165413)],
        [(0.570635,0.000000,0.429365),(0.236734,0.570126,0.193139),(0.167197,0.687133,0.145670),(0.173139,0.667169,0.159693),(0.264911,0.475316,0.259773)],
        [(0.490512,0.000000,0.509488),(0.196309,0.578888,0.224803),(0.135744,0.692358,0.171898),(0.140638,0.663397,0.195965),(0.220709,0.426989,0.352302)],
        [(1.000000,0.000000,0.000000),(0.147944,0.636760,0.215296),(0.089478,0.737358,0.173165),(0.087259,0.704604,0.208137),(0.128691,0.435655,0.435655)]  ]

def sharpness(history):
    ret = 0
    for action in history:
        if action == 'S':
            ret += 1
        elif action == 'P' and ret > 0:
            ret -= 1
    return ret

def action(history_self, history_other):
    sharpness_self = sharpness(history_self)
    sharpness_other = sharpness(history_other)
    if sharpness_self >= 5:
        return 'P'
    elif sharpness_other >= 5:
        return 'S'
    moves = movemap[sharpness_self][sharpness_other]
    v = random.random()
    if v < moves[0]:
        return 'S'
    elif v < moves[0] + moves[1]:
        return 'B'
    else:
        return 'P'

if __name__ == "__main__":
    print(action(history_self, history_other))

This isn't quite the Nash equilibrium (my strategy generator has some instability), but it's close.

For curiosity's sake, here are the estimates of how likely this bot is to win in each game state:

map = [ [0.50000000,0.26337111,0.15970733,0.08144046,0.00000000,0.00000000],
        [0.73662889,0.50000000,0.37879183,0.28035985,0.16622410,0.00000000],
        [0.84029267,0.62120817,0.50000000,0.39441630,0.26038353,0.00000000],
        [0.91855954,0.71964015,0.60558370,0.50000000,0.35246401,0.00000000],
        [1.00000000,0.83377590,0.73961647,0.64753599,0.50000000,0.00000000],
        [1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,0.50000000] ]

Filename: nash.py

To run: python nash.py

Feint

Opens with a quick attack, to test his opponent's defenses.

import sys, random

if len(sys.argv) > 1:
    history_self, history_other = sys.argv[1].split(',')
else:
    history_self = history_other = ""

def sharpness(history):
    ret = 0
    for action in history:
        if action == 'S':
            ret += 1
        elif action == 'P' and ret > 0:
            ret -= 1
    return ret

def action(history_self, history_other):
    sharpness_self = sharpness(history_self)
    sharpness_other = sharpness(history_other)
    if sharpness_self >= 5:
        return 'P'
    elif len(history_self) < 2:
        return 'SP'[len(history_self)]
    elif history_other[1] == 'P':
        # Fierce fight
        if sharpness_self == 0:
            return 'S'
        elif history_self[-(1 + history_self.count('P'))] == 'S':
            return 'P'
        else:
            return 'B'
    else:
        # Smart guy
        if sharpness_other == 1:
            return 'B'
        elif history_self[-1] != 'S' or history_self[-4:] == 'BSBS':
            return 'S'
        elif history_other.count('S') > history_other.count('B'):
            return 'P'
        else:
            return 'B'

if __name__ == "__main__":
    print(action(history_self, history_other))

Filename: feint.py

To run: python feint.py

LatePokeBot

PokeBot's little brother. Never shows weakness, but tries to fight like his big brother.

import sys, random

if len(sys.argv) > 1:
    history_self, history_other = sys.argv[1].split(',')
else:
    history_self = history_other = ""

def sharpness(history):
    ret = 0
    for action in history:
        if action == 'S':
            ret += 1
        elif action == 'P' and ret > 0:
            ret -= 1
    return ret

def action(history_self, history_other):
    sharpness_self = sharpness(history_self)
    return 'SSP'[sharpness_self]

if __name__ == "__main__":
    print(action(history_self, history_other))

Filename: latepokebot.py

To run: python latepokebot.py

Brilliand

Posted 2014-07-23T14:10:56.457

Reputation: 271

You were missing a : in Basilisk; I fixed that for you – Doorknob – 2014-07-25T08:06:21.267

This submission threw some kind of error or exception at some point; you may want to look into fixing that before the next round of trials. (the Basilisk one) – Doorknob – 2014-07-25T08:42:31.907

@Doorknob I chose the starting sequence for Basilisk by looking at the assumptions made by The Watcher and Cave Doctor and finding a sequence that would make those assumptions go badly wrong. Does that violate the "cavemen are fair" rule? – Brilliand – 2014-07-25T16:26:08.957

Go Nash! Win exactly half your matches against all bots smart enough to avoid your P=0 options! – aschepler – 2014-07-29T21:05:31.563

12

FancyTechnoAlgorithm

A fancy techno algorithm for the fancy techno computer program.

Caveman keep lose battle. Caveman angry. So caveman go to computer school learn make algorithm.

import random, sys  # Need import some advanced techno code

if __name__ == '__main__':  # If fancy techno computer program is main

    try:  # Me try use fancy techno algorithm!

        me, he     = sys.argv[1].split(",")
        mePointy   = me.count("S") - me.count("P")
        hePointy   = he.count("S") - he.count("P")
        meCanPoke  = mePointy > 0
        heCanPoke  = hePointy > 0
        meHasSword = mePointy >= 5
        heHasSword = hePointy >= 5
        meScary    = meCanPoke + meHasSword 
        heScary    = heCanPoke + heHasSword

        # Me donno fancy coding math algoritm.
        # Math confuse. Me code work, me happy.
        if he[-6:] == "SB"*3:
            print "SP"[meCanPoke]
        elif (len(he) > 30 and he[-3:].count("B") > 2) or \
             (hePointy > 2 and he.count("SSBB") > 0 and he.count("BBS") > 0):
            if meHasSword:
                print "P"
            else:
                print "SB"[me[-1] == "S"]
        elif hePointy > 3 and he.count("BBS") > 2:
            print "SP"[me[-1] == "S"]
        else:
            print random.choice(\
                [["S", "SP", "P" ],\
                 ["B", "B" , "P" ],\
                 ["S", "P" , "P" ]][heScary][meScary])

    except:  # Fancy techno algorithm Failed... Me just sharpen.
        print "S"

Python 2 program. To run: python fancytechnoalgorithm.py

Vectorized

Posted 2014-07-23T14:10:56.457

Reputation: 3 476

This breaks when there is no input (i.e. on the first turn). I don't know how you want to handle that, so I'm going to have to exclude it from the first round of testing. – Doorknob – 2014-07-23T22:56:14.107

@Doorknob For the first input, is it "," or ""? I guess it's the latter from that. – Vectorized – 2014-07-24T00:33:37.713

For the first input, there will be no arguments (it will be run as python StickSharpener.py). – Doorknob – 2014-07-24T00:37:02.107

@Doorknob I have edited it. Do see if it works now. – Vectorized – 2014-07-24T00:41:11.817

Alright, thanks! I'll include this in the next round of trials. – Doorknob – 2014-07-24T00:42:15.777

@Doorknob Is it ok to change the name of my code ? I have changed quite a bit of the logic. – Vectorized – 2014-07-24T05:54:52.197

That's okay, but notify me when you do so that I can remove the old one. – Doorknob – 2014-07-24T06:45:11.107

@Doorknob Ok done. – Vectorized – 2014-07-24T07:17:40.553

9

PokeBot

Written in Ruby.

puts((ARGV.shift || "P,").match(/(.),/)[1] == "P" ? "S" : "P")

Run with ruby pokebot.rb.

This bot isn't very smart; it does about what the average caveman would do on his own anyway.

Doorknob

Posted 2014-07-23T14:10:56.457

Reputation: 49 044

8

PatientWolf v2.0

Sharpens if dull, pokes if enemy will have a sword next turn or if enemy is dull, blocks otherwise.

my ($me,$him) = split(/,/,$ARGV[0]);
if(!defined $me) {
    print "S";
    exit;
}
my $mysharpness =()= ($me =~ /S/g);
$mysharpness -= ($me =~ /P/g);
my $opponentsharpness =()= ($him =~ /S/g);
$opponentsharpness -= ($him =~ /P/g);
if($mysharpness == 0) {
    print "S";
} elsif($opponentsharpness <= 0 || $opponentsharpness == 4) {
    print "P";
} else {
    print "B";
}

Run with

perl patientwolf.pl

EDIT: thanks to @sylwester for pointing out a bug

killmous

Posted 2014-07-23T14:10:56.457

Reputation: 359

Since you only get one argument with both histories separated by comma you are parsing it wrongly. Eg. PatientWolf.pl SB,SP does a P since it thinks it has sharp stick. – Sylwester – 2014-07-27T14:12:43.207

@Sylwester that's not correct. The first line assigns the first argument to $me and the second argument to $him – killmous – 2014-07-27T18:40:48.163

The CavemanDuel program doesn't use two arguments, only one. eg. perl patientwolf.pl "SB,SP". You should do my($me,$him) = split/,/ $ARGV[0]; and if( @ARGV ) {print "S";exit}. – Sylwester – 2014-07-27T18:59:37.403

@Sylwester ok I see what you're getting at. That wasn't clear from the OP or from the quick glance I had thrown at the controller code. I'll fix that shortly – killmous – 2014-07-27T19:21:06.170

8

Binary Caveman

Sharpen, Stab, Repeat

Based on the idea that blocking is for sissies, this caveman alternates between the two remaining options.

public class BinaryCaveman { 

    public static void main(String[] args) {
        int timelapse = 0;
        if(args.length>0)
        {
            timelapse = ((args[0].length() - 1) / 2);
        }
        switch(timelapse % 2) 
        {
            case 0: System.out.println('S'); 
                    break;
            case 1: System.out.println('P'); 
                    break;
        }
    }
}

Compile with javac BinaryCaveman.java

Run with java BinaryCaveman

EDIT: Adventures in String Arrays..... args.length() throws an error. args.length always returns 1. args[0].length() returns the lengths of the first string in the array.

EDIT 2: Updated thanks to help from Doorknob, Brilliand, and Sylwester. Thanks guys.

Red_Shadow

Posted 2014-07-23T14:10:56.457

Reputation: 191

@MartinBüttner I forgot to divide args - 1 by 2 to only get the number of past submissions by a single player. Fixed that. I cannot understand Dorknob's submission, ruby is practically gibberish to me. Does his always start with sharpening? – Red_Shadow – 2014-07-23T18:17:15.793

Yes, he just checks whether his last move was P or S and does the opposite. And if there is no history yet, he pretends the history would be P, (which then leads him to do S first). – Martin Ender – 2014-07-23T18:21:45.377

Two different approaches that result in the same output. Is that against the rules? – Red_Shadow – 2014-07-23T18:28:57.627

Probably not, I just I'd let you know. – Martin Ender – 2014-07-23T18:30:26.590

There was a bug -- it's args.length, not args.length(). I've fixed that for you. – Doorknob – 2014-07-23T22:49:04.807

This one seems to be a snake - all it outputs is SSSSSSSS. (That's why it got a score of 0 on the leaderboard.) There's probably a bug somewhere. – Doorknob – 2014-07-23T23:59:42.317

2@Doorknob I think it should be args[0].length(), not args.length. – Brilliand – 2014-07-24T09:06:11.617

This submission threw some kind of error or exception at some point; you may want to look into fixing that before the next round of trials. – Doorknob – 2014-07-25T08:42:08.397

It need to be if ( args.length &gt; 0 ) ... since the first run is without any arguments and this throws an exception that is interpreted as B – Sylwester – 2014-07-27T16:24:08.363

7

CavekidBlocks

A crying and frightened cave kid may look like an easy prey. Don't let his pretty face fool you 'cause he knows how to block.

import sys, math, random
def count(a):
    s = 0
    for i in range(len(a)):
        if a[i] == 'P': s-=1
        elif a[i] == 'S': s+=1
        if s < 0: s = 0
    return s
kid = []
scary_adult = []
what2do = 'Sharpen the Stick! Why not? Adult may be doing the same. DONT trust adults!'
if len(sys.argv) > 1:
    kid, scary_adult = sys.argv[1].split(",")
    kid_stick_sharpness = count( kid )
    scary_adult_stick_sharpness = count( scary_adult )
    if (scary_adult_stick_sharpness >= 2):
        what2do = "Block! Block! Block! Adult's stick looks scary sharp."
    elif (kid_stick_sharpness > 0):
        what2do = 'Point your Stick to the adult. It may scary him.'
    else:
        what2do = 'Sharpen the Stick!'

    # Roll d20 for a courage check.
    dice = random.randint(1,20)
    if (dice > 15): what2do = 'Poke the adult! Critical Hit!'
    elif (dice <= 5): what2do = 'Block! Block! Block!'
print(what2do[0])

Run with python3 cavekidblocks.py

ChargerMan

This caveman is very conservative. Will try to charge his weapon and only attacks when needed.

import sys, math, random
def countSharpness(a):
    s = 0
    for i in range(len(a)):
        if a[i] == 'P': s-=1
        elif a[i] == 'S': s+=1
        if s < 0: s = 0
    return s
def getHistory():
    me = ""
    him = ""
    if len(sys.argv) > 1:
        me, him = sys.argv[1].split(",")
    return me,him
if __name__ == '__main__':
    me, him = getHistory()
    me_s = countSharpness(me)
    him_s = countSharpness(him)
    answer = 'B'
    # First Case
    if (len(me) == 0):
        answer = 'S'
    # I have a sword
    elif (me_s == 5):
        answer = 'P'
    # Cant let he gets a sword
    elif (him_s == 4):
        answer = 'P'
    # His sword is dull
    elif (him_s == 0):
        # He may try to sharp
        # Cant attack? Sharp my stick
        if (me_s == 0): answer = 'S'
        else:
            if (random.randint(0,33) != 0): answer = 'S'
            else: answer = 'P'
    elif (len(him) % 5 == 0):
        # Decide what to do based on the
        # opponent last 3 movements.
        hist = him[-3:]
        # Does he like to block?
        if (hist.count('B') >= 2): answer = 'S'
    print(answer)

Run with python3 chargerman.py

Trickster

Trickster doesn't know how to fight, so he tries to confuses other caveman.

import sys, math
a = "PPS"
i = 0
if (len(sys.argv) > 1): i = math.floor(((len(sys.argv[1])-1)/2) % 3)
print(a[i])

Run with python3 trickster.py

Unfortunately, after the commit acc74, Trickster doesnt work as planned anymore.

wendelbsilva

Posted 2014-07-23T14:10:56.457

Reputation: 395

4That trickster program is evil – Nexus – 2014-07-23T23:03:34.860

@Nexus I though so too. Unfortunately Trickster isnt doing well in the duels. – wendelbsilva – 2014-07-24T15:05:36.963

6

Hodor

Hodor is not very aggressive. He likes to stay in his shield unless there's a good opportunity to strike.

compile with: javac Hodor.java and run with: java Hodor

code:

public class Hodor {
    public static void main(String[] args){

        String previousMoves = null;

        //account for no input
        if(args.length == 0){
            System.out.print('S');
            System.exit(0);
        }else{
            previousMoves = args[0];
        }

        //declare variables
        char action = 'S';
        int enemySharpens = 0, enemyPokes = 0, myPokes = 0, mySharpens = 0;
        String[] movesArray = previousMoves.split(",");
        char[] enemyMoves = movesArray[1].toCharArray(), myMoves = movesArray[0].toCharArray();

        //determine enemy sharpness
        for(int i=0; i<enemyMoves.length; i++){
            if(enemyMoves[i] == 'S'){
                enemySharpens++;
            }else if(enemyMoves[i] == 'P'){
                enemyPokes++;
            }
        }

        //block if opponent can poke, else sharpen
        if(enemySharpens - enemyPokes > 0){
            action = 'B';
        }else{
            action = 'S';
        }

        //determine my sharpness
        for(int i=0; i<movesArray[0].length(); i++){
            if(myMoves[i] == 'P'){
                myPokes++;
            }else if(myMoves[i] == 'S'){
                mySharpens++;
            }
        }

        //poke as much as possible if the game is about to end
        if((mySharpens-myPokes) > (100-enemyMoves.length)){
            action = 'P';
        }

        try{
            //sharpen if opponent blocks 2 times in a row and I didn't just sharpen
            if((enemyMoves[enemyMoves.length-1] == 'B') && (enemyMoves[enemyMoves.length-2] == 'B') && (myMoves[myMoves.length-1] != 'S')){
                action = 'S';
            }
            //poke if opponent sharpens twice in a row
            if((enemyMoves[enemyMoves.length-1] == 'S') && (enemyMoves[enemyMoves.length-2] == 'S')){
                action = 'P';
            }
            //poke if the opponent just sharpened/blocked then poked, has a blunt stick, and my stick isn't blunt
            if((enemyMoves[enemyMoves.length-2] != 'P') && (enemyMoves[enemyMoves.length-1] == 'P') && (enemySharpens-enemyPokes == 0) && (mySharpens - myPokes > 0)){
                action = 'P';
            }
        }catch (ArrayIndexOutOfBoundsException e){
            //not enough info
        }

        //poke if we have a sword
        if(mySharpens-myPokes > 4){
            action = 'P';
        }

        System.out.print(action);
    }
}

Edit: minor code update

Qwix

Posted 2014-07-23T14:10:56.457

Reputation: 121

This submission threw some kind of error or exception at some point; you may want to look into fixing that before the next round of trials. – Doorknob – 2014-07-26T04:00:55.243

1Try with SB,BB. When other cavemen misbehaves on the first turn Hodor misbehave too. – Sylwester – 2014-07-27T16:36:47.303

5

PatientBlacksmith

This bot is written in R, use Rscript PatientBlacksmith.R to trigger it .

args <- commandArgs(TRUE)
if(length(args)){
    input <- strsplit(strsplit(args,split=",")[[1]],"")
    me <- input[[1]]
    opponent <- input[[2]]
    sharpness <- 0
    for(i in seq_along(opponent)){
        if(opponent[i]=="S") sharpness <- sharpness + 1
        if(opponent[i]=="P") sharpness <- sharpness - 1
        }
    out <- ifelse(sharpness>0,"B","S")
    bfree <- me[me!="B"]
    r <- rle(bfree) #run length encoding
    S_sequence <- r$length[r$value=="S"]
    P_sequence <- r$length[r$value=="P"]
    if(!length(P_sequence)) P_sequence <- 0
    if(tail(S_sequence,1)==5 & tail(P_sequence,1)!=5) out <- "P"
}else{out <- "S"}
cat(out)

Measures the opponent stick sharpness: blocks when sharp, take time to sharpen otherwise. When own sharpness reaches 5, poke until sharpness is gone.

plannapus

Posted 2014-07-23T14:10:56.457

Reputation: 8 020

This breaks when given no input (i.e. on the first turn); I don't know how to fix it so I'm going to have to exclude it from round 1 of testing. – Doorknob – 2014-07-23T22:50:42.887

@Doorknob corrected. – plannapus – 2014-07-24T05:51:00.817

5

Swordsmith

Need sharp stick. If have sharp stick, poke. Me no feel pain.

program Swordsmith
   implicit none
   integer :: mySharp,ierr,arg_count
   logical :: lExist
   character(38) :: filename = "players/Swordsmith/SwordsmithSharp.txt"

! check argument counts for initialization of storage file
   arg_count = command_argument_count()
   if(arg_count == 0) then
      inquire(file=filename,exist=lExist)
      mySharp = 0
      if(lExist) then
         open(unit=10,file=filename,status='replace')
      else
         open(unit=10,file=filename,status='new')
      endif
      write(10,*) mySharp
      close(10)
   endif

! open, read, & close the file for mySharp
   open(unit=10,file=filename,status='old')
   read(10,*) mySharp
   close(10)

! make decision
   if(mySharp < 5) then
      print '(a1)',"S"
      open(unit=10,file=filename,status='replace')
      mySharp = mySharp + 1
      write(10,*) mySharp
      stop
   endif
   print '(a1)',"P"
end program Swordsmith

Save as swordsmith.f90 and compile with gfortran -o swordsmith swordsmith.f90, execute as you would any normal executable: ./swordsmith.

Kyle Kanos

Posted 2014-07-23T14:10:56.457

Reputation: 4 020

This appears to print a space () before the real output. I have no idea how to fix that, so I'm going to have to exclude this submission from the first round of testing. – Doorknob – 2014-07-23T22:55:29.540

Also, I've corrected your file path; turns out the current working directory when they run isn't your program's. Oh, and if by "new instance" you mean "each game," I can't do that because that would require special-casing the controller program; you may want to do that in your own code. – Doorknob – 2014-07-23T23:11:39.950

@Doorknob: I've updated my code: output is a single character, it deletes an already-existing file at first run, and the file is in the player directory. – Kyle Kanos – 2014-07-24T00:22:59.257

Alright, thanks! This submission is now included in the leaderboard. – Doorknob – 2014-07-24T03:04:07.473

@Doorknob: Cool! Sucks that I'm not first. Also: I'm pretty sure Fortran users are like Vampires, so I'm pretty sure you're going to start coding in Fortran soon! Muahahahaha! – Kyle Kanos – 2014-07-24T03:05:27.623

5

Speculative Sylwester - Perl5

Speculative Sylwester wants to take out sword seekers by looking at the patterns and poke when there is a chance opponent will sharpen and sharpen when opponent is most likely to block. However, he will not do that if there is a chance that he would have guessed that himself will sharpen in the next move and we are even more cautious when we decide to sharpen.

As for when opponent is blunt he tries to be aggressive but will eventually start to save for a sword when that seems fruitless.

#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

## Valid operations
my $SHARPEN = "S";
my $POKE    = "P";
my $BLOCK   = "B";

## It will also print resolution to stderr
my $VERBOSE = 0;

my $first_move = not @ARGV;
my ($me, $you) = split(',', $ARGV[0]) unless( $first_move );

## What do I do?
me_do($SHARPEN, "beginning") if $first_move;
me_do($POKE, "end is near") if  almost_over() || sword($me);
me_do($SHARPEN, "you sword") if !sword($me) && sword($you);
me_do($POKE, "you repeat") if consecutive_sharpens($you) && sharp($me);
me_do(blunt_move(), "you blunt stick") if not sharp($you); 
me_do(aggressive_move(), "me think you sharpen") if sharpen_next($you) && !sharpen_next($me);
me_do($SHARPEN, "me think you block") if you_block_next() && very_little_chance_me_sharpen_next();
me_do($BLOCK, "me have no idea you do");

sub almost_over {
  sharp($me) >= (100 - length($you));
}

sub sharp {
  my $history = shift;
  my $sharp = 0;
  foreach my $s ( split('',$history) ) {
    $sharp++ if( $s eq "S");
    $sharp-- if( $s eq "P" && $sharp > 0);
  }
  return $sharp;
}

sub sword {
  my $me = shift;
  sharp($me) >= 5;
}

sub num_pokes {
  my $me = shift;
  $me =~ s/[^P]//g; #/ SO highlight bug?
  length($me);
}

sub consecutive_sharpens {
  my $you = shift;
  $you =~ m/SS+$/
}

sub sharpen_next {
  my $you = shift;
  $you =~ /([^S]+)S\1S\1$/;
}

sub you_block_next {
  $you =~ /([^B]+B*)B\1B\1$/ || $you =~ /B{4}$/;
}

sub very_little_chance_me_sharpen_next {
  $me !~ /S$/ && ( $me !~ /([^S]+)S\1$/ || $me =~ /^SB+SB+$/ ); 
}

sub blunt_move {
  my $sword_move = sword($me) ? $POKE : $SHARPEN;
  ( $me =~ m/(?:PS){5,}/ || sharp($me)*7 < num_pokes($me) ? $sword_move : aggressive_move() );
}

sub aggressive_move {
  sharp($me)? $POKE : $SHARPEN;
}

sub me_do {
  my ($stick_operation, $reason) = @_;
  my $arg = ( $first_move ? "" : "$me,$you" );
  my $resolution = "$stick_operation me do because $reason ($arg)";
  print "$resolution\n";
  err($resolution);
  exit;
}

sub err {
  my($str) = @_;
  print STDERR "SpeculativeSylwester:$str\n" if $VERBOSE;
}

To run on linux just add this in playerlist.txt:

perl players/SpeculativeSylwester/SpeculativeSylwester.pl

Facile Fibonacci - R6RS Scheme

Besides the first move Facile Fibonacci blocks when the turn is a Fibonacci number (starting from 0) and fills the rest with PPSS.. and changes when passes 8 to an endless sequence of PSS to win with a sword.

#!r6rs
(import (rnrs base)
        (only (rnrs) fold-left display command-line))

(define %SHARPEN "S")
(define %POKE    "P")
(define %BLOCK   "B")

(define (fibonacci? n)
  (let loop ((a 1) (b 1))
    (cond ((> a n) #f)
          ((= a n) #t)
          (else (loop b (+ a b))))))

(define (poke? num-sp)
  (if (< num-sp 8)
      (even? (div num-sp 2))
      (= 2 (mod num-sp 3))))

(define (split-string x)
  (let ((len (div (string-length x) 2)))
    (substring x 0 len)))

(define (num-sp x)
  (fold-left (lambda (a x)
               (if (eqv? x #\B) a (+ a 1)))
               0
               (string->list x)))

(define (advanced-strategy me)
  (cond ((fibonacci? (string-length me)) %BLOCK)
        ((poke? (num-sp me)) %POKE)
        (else %SHARPEN)))

(define (decide args)
  (if (= (length args) 1)
      %SHARPEN
      (advanced-strategy (split-string (cadr args)))))

;; The dirty imperative code:
(display (decide (command-line)))

To run just install ikarus with apt-get install ikarus and add this in playerlist.txt:

ikarus --r6rs-script players/FacileFibonacci/FacileFibonacci.scm

Studious Sylwester - Perl5

Studious Sylwester uses the same tactic as Speculative Sylwester, but he also looks at previous games to determine where he might have taken a wrong choice.

#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

## Valid operations
my $SHARPEN = "S";
my $POKE    = "P";
my $BLOCK   = "B";

## It will also print resolution to stderr
my $VERBOSE = 0;

my $path = $0; # "players/StudiousSylwester/StudiousSylwester.pl";
my $first_move = not @ARGV;
my ($me, $you) = split(',', $ARGV[0]) unless( $first_move );

## What do I do?
me_do($SHARPEN, "beginning") if $first_move;
me_do(consult_history($POKE, "end is near")) if  almost_over() || sword($me);
me_do(consult_history($SHARPEN, "you sword")) if sword($you);
me_do(consult_history($POKE, "you repeat")) if consecutive_sharpens($you) && sharp($me);
me_do(consult_history(blunt_move(), "you blunt stick")) if not sharp($you);
me_do(consult_history(aggressive_move(), "me think you sharpen")) if sharpen_next($you) && !sharpen_next($me);
me_do(consult_history($SHARPEN, "me think you block")) if you_block_next() && very_little_chance_me_sharpen_next();
me_do(consult_history($BLOCK, "me have no idea you do"));

sub almost_over {
  sharp($me) >= (100 - length($you));
}

sub sharp {
  my $history = shift;
  my $sharp = 0;
  foreach my $s ( split('', $history) ) {
    $sharp++ if( $s eq "S");
    $sharp-- if( $s eq "P" && $sharp > 0);
  }
  return $sharp;
}

sub sword {
  my $me = shift;
  sharp($me) >= 5;
}

sub num_pokes {
  my $me = shift;
  $me =~ s/[^P]//g; #/ SO highlight bug?
  length($me);
}


sub consecutive_sharpens {
  my $you = shift;
  $you =~ m/SS+$/
}

sub sharpen_next {
  my $you = shift;
  $you =~ /([^S]+)S\1S\1$/;
}

sub you_block_next {
  $you =~ /([^B]+B*)B\1B\1$/ || $you =~ /B{4}$/;
}

sub very_little_chance_me_sharpen_next {
  $me !~ /S$/ && ( $me !~ /([^S]+)S\1$/ || $me =~ /^SB+SB+$/ );
}

sub blunt_move {
  my $sword_move = sword($me) ? $POKE : $SHARPEN;
  ( $me =~ m/(?:PS){5,}/ || sharp($me)*7 < num_pokes($me) ? $sword_move : aggressive_move() );
}

sub aggressive_move {
  sharp($me)? $POKE : $SHARPEN;
}


sub consult_history {
  my ($suggested_move, $why) = @_;
  my $mylen = length($me);

  # By demanding 5 or more there are 81 (- illegals)
  # different possibilities. Below that and
  # we are shooting in the dark.
  return @_ if( $mylen <= 4 );

  my $override = $suggested_move;
  my @lines = ();
  my %matches      = (P => 0, B=> 0, S=> 0);
  my %match_prefix = (P => 0, B=> 0, S=> 0);
  my $file = "$path.prefix";
  my $sem = "$path.sem";
  my $found_session = 0;

  # Since Judge is running multiple instances at the same time we flock
  open(LOCK, "> $sem") || die ("$path error while open $sem: $!");
  flock(LOCK, 2);

  if( -e $file ) {
    open(FH, $file) || die("$path: error while open $file: $!");

    my $prevyou = substr($you,0,-1);
    while(my $ln = <FH>){
      if ( $ln =~ m/^$me(.).*,$you(.?).*$/ ) {
         # Match that ends here is either a win or a loss depending on my choice
     my $key = ($2 eq "" ? ( $1 eq $POKE ? $SHARPEN : $POKE ) : $2);
     $matches{$key}++;
     $match_prefix{$1}++;
      }
      if( $ln =~ m/^$me,$prevyou$/ ) {
        $found_session++;
    next;
      }
      $found_session++ if( $ln =~ m/^$me.*,$prevyou.*$/ );
      push @lines,$ln;
    }
  }

  my $num_matches = (grep { $matches{$_} != 0 } keys %matches);
  unless( $num_matches || $found_session || $mylen == 5 ) {
    err("WARNING: You have not started this game from the beginning. This will not be a valid outcome! ($me,$you)");
  }

  if( $num_matches == 1 ) {
    my $match_val = (grep { $matches{$_} != 0 } keys %matches)[0];
    if( $match_val eq $BLOCK && !sharp($me)) {
      $override = $SHARPEN;
      $why = "me know u block";
    } elsif ( $match_val eq $SHARPEN ) {
      $override =  aggressive_move();
      $why = "me know u sharpen";
    } elsif ( $match_val eq $POKE && !sword($me) ) { 
      $override = $BLOCK;
      $why = "me know u poke";
    }

  } elsif($num_matches > 1 && $mylen > 6 ) {
    # if the chances are overwelming we are not poked we might as well sharpen
    # if we are wrong here we loose
    if( $matches{$POKE} * 4 < ($matches{$BLOCK}+$matches{$SHARPEN}) && !sword($me)){
      $override = $SHARPEN;
      $why = "me think u block/sharpen";
    }
    # if chances for sharpening is higher than poke/block we go for it with any stick
    if( $matches{$SHARPEN} > 2*($matches{$BLOCK}+$matches{$POKE}) && sharp($me) ) {
      $override = $POKE;
      $why = "me think u sharpen";
    }

    # if the chances for poke is overwelming, we might consider blocking
    if( $matches{$POKE} > 2*($matches{$BLOCK}+$matches{$SHARPEN}) && !sword($me)){
      $override = $BLOCK;
      $why = "me think u poke";
    }
  }

  unless ( $match_prefix{$override} ) {
    open( FH, "> $file") ||     die("$path: error while open $file: $!");
    push @lines, "$me$override,$you\n";
    foreach my $line ( sort @lines ) {
      print FH $line;
    }
  }

  my $stats = join("",map {"$_=>$matches{$_} "} keys %matches);

  if( $override ne $suggested_move ) {
     $why .= ". stats: $stats, original choice: $suggested_move";
  }

  close FH;
  close LOCK;

  return ( $override, $why );
}

sub me_do {
  my ($stick_operation, $reason) = @_;
  my $arg = ( $first_move ? "" : "$me,$you" );
  my $resolution = "$stick_operation me do because $reason ($arg)";
  print "$resolution\n";
  err($resolution);
  exit;
}

sub err {
  my($str) = @_;
  print STDERR "StudiousSylwester:$str\n" if $VERBOSE;
}

To run on linux just add this to playerlist.txt

perl players/StudiousSylwester/StudiousSylwester.pl

Studious edit

I can't reproduce the problems you had with $0 not being the full path to the perl script when it's run with perl. I have also pulled your changes and I see no changes in the CavemanDuels src and It's the same I've been running 20+ times without the problem you are reporting. I'm starting to fear you might have sourced the script as a bash script instead of running it while executable or as an argument to perl. I need more info to actually know for sure. As a test I did this and you can do the same to see if you get the same result:

echo '#!/usr/bin/perl
print "$0\n\n";' > testcmd.pl;
perl ./testcmd.pl;           # outputs ./testcmd.pl
bash -c "perl ./testcmd.pl"; # outputs ./testcmd.pl
bash -c ./testcmd.pl;        # outputs an error since it's not executable
chmod 755 ./testcmd.pl;
./testcmd.pl;                # outputs ./testcmd.pl
bash -c ./testcmd.pl;        # outputs ./testcmd.pl since it's executable

Sylwester

Posted 2014-07-23T14:10:56.457

Reputation: 3 458

Scheme doesn't seem to want to cooperate with me on my machine, so I've been unable to test the Fibonacci one. I'll keep trying to get it to work, but it would be great if you could translate it to another language. – Doorknob – 2014-08-03T01:35:36.963

The studious one also doesn't seem to work, because $0 is bash when called from a bash command line (which the controller does). You could just hardcode players/StudiousSylwester/foo.txt, though. – Doorknob – 2014-08-03T01:41:01.353

@Doorknob I've added how to install ikarus and I've added my thoughts on $0 for Studious. – Sylwester – 2014-08-04T16:12:34.650

4

I call him JavaMan

compile: javac JavaMan.java
run: java JavaMan SPB,SBB

note: I don't intend to play code golf.. but if you are a golfer and the spaces / extra lines make your eyes bleed.. feel free to change it

public class JavaMan
{
    public static void main(String[] args)
    {
        // input: SPB,SBB
        // me, enemy
        // S: sharpen, P: poke, B: block

        if (args.length == 0)
        {
            System.out.println("S");
        }
        else
        {
            String[] states = args[0].split(",");
            Player me = new Player(states[0].toCharArray());
            Player enemy = new Player(states[1].toCharArray());  //fixed thanks to Roy van Rijn

            if (me.hasSword())
            {
                System.out.println("P");
            }
            else if (!enemy.canPoke())
            {
                if (me.canPoke() && (Math.random() * 95) < states[0].length())
                {
                    System.out.println("P");
                }
                else
                {
                    System.out.println("S");
                }
            }
            else if (enemy.hasSword())
            {
                if (me.canPoke())
                {
                    System.out.println("P");
                }
                else
                {
                    System.out.println("S");
                }

            }
            else if (enemy.canPoke())
            {
                if (me.canPoke())
                {
                    if ((Math.random() * 95) < states[0].length())
                    {
                        System.out.println("P");
                    }
                    else
                    {
                        System.out.println("B");
                    }
                }
                else
                {
                    if ((Math.random() * 95) < states[0].length())
                    {
                        System.out.println("S");
                    }
                    else
                    {
                        System.out.println("B");
                    }
                }
            }
            else
            {
                System.out.println("S");
            }
        }
    }

}

class Player
{
    int sharpLevel;

    public Player(char[] state)
    {
        sharpLevel = 0;
        for (char c : state)
        {
            switch (c)
            {
            case 'S':
                sharpLevel++;
                break;
            case 'P':
                sharpLevel--;
                break;
            case 'B':
                break;
            default:
                System.out.println(c);
            }
        }
    }

    public boolean hasSword()
    {
        return sharpLevel > 4;
    }

    public boolean canPoke()
    {
        return sharpLevel > 0;
    }
}

user2813274

Posted 2014-07-23T14:10:56.457

Reputation: 181

4Submissions for King of the Hill challenges are not meant to be golfed, so don't worry. ;) – Martin Ender – 2014-07-23T17:45:45.977

I've changed its name to JavaMan, because "Caveman" is a bit too generic to be in the leaderboard. Hopefully that's okay with you; if not, just change it to something else. – Doorknob – 2014-07-23T22:26:50.800

1This breaks when given no input (i.e. on the first turn); I don't know how you want to handle that so I'm going to have to exclude it from the first round of testing. – Doorknob – 2014-07-23T22:52:12.647

Fixed, and the name change is fine with me – user2813274 – 2014-07-24T14:07:26.693

I'm getting this compile error: JavaMan.java:1: error: illegal character: \65279 – Doorknob – 2014-07-25T07:42:17.670

Never mind; it was an encoding issue on my part. Don't worry; it works fine. – Doorknob – 2014-07-25T08:14:53.697

what makes my eyes bleed in the line feed before the { and else. this is java, not C or whatever. – njzk2 – 2014-07-25T14:22:26.983

1I think you've got a mistake in parsing the state, both 'me' and 'enemy' get the same moves: states[0] – Roy van Rijn – 2014-07-28T13:12:40.050

@RoyvanRijn ah, your right, thanks for catching that – user2813274 – 2014-07-28T13:50:34.313

@njzk2 the braces / else aren't quite un-standard for java [http://www.javaranch.com/styleLong.jsp#indent ] , although to be honest I prefer that format more than the one where the else { is on the same line since I come from an xml background where all the tags line up nicely

– user2813274 – 2014-07-28T14:16:23.120

@user2813274: I tend to refer to oracle recommendations (http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-142311.html#449) rather that javaranch. Of course this is 15 years old and some of the recommendations are no longer really valid.

– njzk2 – 2014-07-28T14:21:26.460

4

Prison Rules, Haskell

Cavewoman think caveman and other caveman should talk, share stick. But, hey ho, if must fight, fight prison rules. Find boss and attack.

ViceLeader Alpha Caveman now; that who caveman must fight. Other cavemen fight later. If my caveman lose, no worry; he too hairy anyway.

import System.Environment


-- Tell caveman next move

next move
    | end with sharp stick  = poke with (what have)
    | they no poky          = sharpen stick
    | me have sword         = poke with sword
    | soon them have sword  = try poke or sharpen
    | soon have own sword   = fear pokes
    | think them want sword = sharpen stick
    | getting bored now     = sharpen stick
    | otherwise             = block poky stick


-- How fancy techno computer program know?

    where
        end with sharp stick = pokiness my stick >= moves before fight boring
        they no poky  = pokiness their stick == 0
        me have sword = pokiness my stick >= 5
        soon "them" have sword = pokiness their stick == 4
        soon have "own" sword  = pokiness my stick == 4
        try poke or sharpen = if pokiness my stick > 0
                              then poke with stick
                              else sharpen stick
        fear pokes = count 2 (block poky stick) and (sharpen stick)
        think them want sword = pokiness their stick == 3
        getting bored now = those last 2 mine same

        what have
            | me have sword = sword
            | otherwise     = stick



-- Rest not for caveman - only techno computer

        moves before time up = time - (length . fst $ move)

        and   = my
        mine  = my
        my    = fst move
        their = snd move

        before = "before"
        bored  = "bored"
        boring = "boring"
        have   = "have"
        no     = "no"
        now    = "now"
        own    = "own"
        pokes  = "pokes"
        same   = "same"
        sharp  = "sharp"
        them   = "them"
        want   = "want"


fight = 100


main = do
    movesHistoryEtc <- getArgs
    putStrLn . next . basedOn $ movesHistoryEtc


basedOn = movesOfEachCaveman . history

history []    = ""
history (h:_) = h

movesOfEachCaveman "" = ("", "")
movesOfEachCaveman h  = (\(a, b) -> (a, tail b)) . span (/= ',') $ h


sharpened = 'S'
poked     = 'P'
blocked   = 'B'

times m = length . filter (== m)


with  = "WITH"
poky  = "POKY"
sword = "SWORD"
stick = "STICK"

sharpen stick    = "SHARPEN " ++ stick
block poky stick = "BLOCK " ++ poky ++ " " ++ stick
poke with stick  = "POKE " ++ with ++ " " ++ stick


pokiness stick is = foldl countPokiness 0 stick

countPokiness pokyPoints 'P'
    | pokyPoints > 0         = pokyPoints - 1
    | otherwise              = 0
countPokiness pokyPoints 'S' = pokyPoints + 1
countPokiness pokyPoints  _  = pokyPoints


allLast n x xs = all (== x) $ take n . reverse $ xs

those previous n moves same = ((length moves) >= n)
                           && (allLast n (last moves) moves)

count n firstMoves moveHistory lastMove = if allLast n fm moveHistory
                                          then lastMove
                                          else firstMoves
    where fm = head firstMoves

Written in Haskell (go functional programming!), so save as prisonrules.hs, then compile with:

ghc prisonrules.hs

And run as:

prisonrules [history]

comperendinous

Posted 2014-07-23T14:10:56.457

Reputation: 456

3

Aichmophobic - Lua

He'll occasionally poke you, but only until the some stick gets too sharp. When this happens, he'll panic and curl into fetal position.

if arg[1] == nil then
  response = "S"
elseif not arg[1]:match('SSSSS') == nil then
  --PANIC
  response = "B"
else  
  --Minds his own business and goes where he pleases
  math.randomseed(os.time())
  local rand = math.random();

  response = rand > 0.6 and "P" or "S"
end

print(response)

Run it with:

lua aichmophobic.lua

William Barbosa

Posted 2014-07-23T14:10:56.457

Reputation: 2 889

2Your output has to be in capital letters; I've fixed that for you. (Also, I misspelled the name of this submission about a thousand times. :P) – Doorknob – 2014-07-23T22:34:59.560

3

Nigel

Nigel is a patient, defensive old caveman who would rather be tactical than go all out on the attack.

It's a PHP script, call with php nigel.php

<?php
// Seed the random number generator
srand(time());

// Simple function output chosen move
function move($m)
{
    echo $m;
    echo "\n";
    exit;
}

// Make stick sharp if first move
if (sizeof($argv) == 1)
    move("S");

// Grab the list of moves
$moves = explode(",", $argv[1]);    
$mySharpness = 0;
$opSharpness = 0;

// Loop through all previous moves and calculate sharpness
for ($i=0; $i<strlen($moves[0]); $i++)
{
    $myMove = substr ($moves[0], $i, 1);
    $opMove = substr ($moves[1], $i, 1);
    if ($myMove == "S")     $mySharpness++;
    if ($opMove == "S")     $opSharpness++; 
    if ($myMove == "P" && $mySharpness > 0)     $mySharpness--;
    if ($opMove == "P" && $opSharpness > 0)     $opSharpness--;     
}

// We somehow have a sword.. ATTACK!
if ($mySharpness > 4)
    move("P");

// Opponent is blunt, guarenteed upgrade!
if ($opSharpness < 1)
    move("S");          

// If we're sharp, either block or poke, unless OP is near a sword
if ($mySharpness > 0)
{
    // Oppenent is halfway to a sword.. ATTACK!
    if ($opSharpness > 2)
        move("P");  

    if (rand(0,1) == 0)     move("P");
    else                    move("B");
}

// If we're blunt, either sharpen or block
else
{
    if (rand(0,1) == 0)     move("S");
    else                    move("B");  
}

?>

ArcticanAudio

Posted 2014-07-23T14:10:56.457

Reputation: 111

3

Bob Caves

Bob Caves is one of the most clever guys in his cave. He has learned to count with one hand (the other is occupied in holding his stick). He has known of this Stone Age Olympics and wanted to participate.

His main strategy is block and sharpen his stick until he has a nice sharpy stick or the other caveman has a sharpy one too. In this case Bob Caves tries to poke him!

import java.util.Random;

public class BobCaves {

    public static void main(String[] args) {
        int mySharpness = 0;
    int otherSharpness = 0;

    //Boc counts
    if (args.length > 0) {
        String[] ss = args[0].split(",");
        mySharpness = howSpiky(ss[0]);
        otherSharpness = howSpiky(ss[1]);
    }
    // Bob thinks!
    Random rn = new Random();
    if (mySharpness == 0 && otherSharpness == 0){
        System.out.println( "S");
    }
    if (otherSharpness == 0 && mySharpness < 5 && mySharpness > 0){
        if (rn.nextBoolean()){
            System.out.println("P");
        } else {
            System.out.println("S");
        }
    } 

    if (mySharpness >= 5 || (otherSharpness >= 2 && mySharpness > 0)) {
        System.out.println("P");
    }

    if (rn.nextInt(5) > 3) {
        System.out.println("S");
    } 

    System.out.println("B");
    }

    private static int howSpiky(String s1) {
        int count = 0;
        char[] c1 = s1.toCharArray();
        for (int i = 0; i < c1.length; i++) {
        if (c1[i] == 'S') {
                count++;
            } else if (c1[i] == 'P'){
                count --;
            }
        }
        return count;
    }

}

Compile with javac BobCaves.java and run with java BobCaves

Edit: Bob now counts when there is any block! (thanks to Mikey Mouse). Also he will sharp his stick when the other caveman stick is blunt.

Edit 2: Improved count method (thanks again to Mikey).

Edit 3: Making Bob slightly more aggressive.

Averroes

Posted 2014-07-23T14:10:56.457

Reputation: 2 298

2Bob forget count effect of Poke:Block on own stick sharpness. Three "S"s in s no mean stick 3 times sharp. Every "P" in s mean unsharpened stick. Huh huh huh... "Pee" cave man joke... – Mikey Mouse – 2014-07-24T12:02:32.500

@MikeyMouse Bob concurs. Bob will visit the witch doctor to improve his technique. Bob grateful! – Averroes – 2014-07-24T12:07:19.747

1Witch doctor teach Bob good. But him forget mention Poke:Poke scenario. Stick get blunt then too. Bob not need consider opponent move. If Bob Poke, stick get blunt. Either blunt on: opponent poke, on opponent block or on opponent head. If on opponent Head, Bob win and can dance around cave with blunt stick. – Mikey Mouse – 2014-07-24T12:53:48.353

1@MikeyMouse Bob knows how to count. Bob needs learn to read. Thanks again! – Averroes – 2014-07-24T19:32:04.037

3

Is it a bird? Is it a plane? It's RegExMan!

He tries to analyze your super-boring sequences with his special primeval RegEx-power!

#!/usr/bin/env python
import sys, re

def whatAmIDoing(opnHist, meSharp, opnSharp) :

    match = re.search(r"([PSB]{3,})\1$", opnHist)    ### Super RegEx ftw!

    if meSharp >= 5 :
        return "P"
    if opnSharp == 4 and meSharp > 0 :
        return "P"
    if match :
        opnStrat = match.group()
        if opnStrat[0] == "S" :
            if meSharp > 0 :
                return "P"
            else :
                return "S"
        elif opnStrat[0] == "B" :
            return "S"
    if opnSharp <= 0 :
        return "S"
    return "B"

try :
    hist = sys.argv[1].split(",")
    sharp = map(lambda h : h.count("S") - h.count("P"), hist)
    answer = whatAmIDoing(hist[1], *sharp)
except Exception :
    answer = "S"
finally :
    print(answer)

Written in Python 2.7, run with python RegExMan.py [history]

Cipher

Posted 2014-07-23T14:10:56.457

Reputation: 791

3

Sicillian

But it's so simple! All I have to do is divine from what I know of other caveman: is he the sort of caveman who would block, sharpen, or poke? Now, a clever caveman would poke or block, because he would know that only a great fool would sharpen and expose himself to attack. I am not a great fool, so I can clearly not sharpen. But other caveman must know I am not a great fool, and would have counted on it, so I can clearly not poke or block!

Run with:

javac Sicillian.java
java Sicillian

Code:

public class Sicillian {

    public static void main(String[] args) {

        if (args.length == 0) System.out.println("S");
        else {
            //get and analyze history
            String[] history = args[0].split(",");
            Caveman vizzini = new Caveman(history[0].toCharArray());
            Caveman fool = new Caveman(history[1].toCharArray());
            Think divine = new Think(history[0].toCharArray(),history[1].toCharArray());

            //The Sicillian always thinks and makes a logical decision before acting...
            char onlyAFool = divine.clearly(vizzini.getSharpness(),fool.getSharpness());

            //Never go in against a Sicillian when death is on the line!
            if(onlyAFool == 'S') {
                if(!vizzini.weaponless()) poke();
                else sharpen();
            }
            else if(onlyAFool == 'P') {
                if(vizzini.hasSword()) poke();
                else block();
            }
            else if(onlyAFool == 'B') sharpen();

            else {          // Inconceivable!

                //if he's a sharpener, poke him where it hurts!
                if(fool.isSharpener()) {
                    if(vizzini.getSharpness() >= 2) poke();  //don't ever go weaponless, else you give him the advantage
                    else sharpen();
                }               
                //if he's a blocker, get sword and break through his defense
                else if(fool.isDefensive()) {
                    if(vizzini.hasSword()) poke();
                    else sharpen();
                }
                // fool doesn't have a disposition to do anything in particular
                else {
                    //he could be sharpening and blocking to get a sword in which case his sharpness will be higher
                    //or a random, which will average a lower sharpness
                    if (fool.getSharpness() <= 2) { //assume random
                        if(vizzini.hasSword()) poke();
                        else if(fool.weaponless()) sharpen();
                        else block();
                    }
                    else {
                        if(vizzini.hasSword()) poke();
                        else if(vizzini.getSharpness() > fool.getSharpness()) sharpen();    //we can win race to sword
                        else if(vizzini.getSharpness() >= 2 || (!vizzini.weaponless() && fool.onEdge())) poke();
                        else sharpen();
                    }
                }
            }           
        }
    }   //end of main

    private static void poke() {
        System.out.println("P");
    }
    private static void block() {
        System.out.println("B");
    }
    private static void sharpen() {
        System.out.println("S");
    }
}
class Think {
    private char[][] cleverman = new char[6][6];    //tracks what the enemy does in a particular situation 
    private int mySharpness;
    private int enemySharpness;
    public Think(char[] myAction, char[] enemyAction) {
        //init variables
        mySharpness = 0;
        enemySharpness = 0;

        for(int i = 0; i < myAction.length; i++) {
            //remember what enemy did last time
            cleverman[mySharpness][enemySharpness] = enemyAction[i];
            //System.out.println("When I was at ("+mySharpness+") and he was at ("+enemySharpness+") he did ("+enemyAction[i]+")");

            //calculate my sharpness
            if(myAction[i] == 'S') mySharpness++;
            else if(myAction[i] == 'P') mySharpness--;
            if(mySharpness < 0) mySharpness = 0; //ensure multiple pokes don't create a negative sharpness
            //calculate my enemy's sharpness
            if(enemyAction[i] == 'S') enemySharpness++;
            else if(enemyAction[i] == 'P') enemySharpness--;
            if(enemySharpness < 0) enemySharpness = 0; //ensure multiple pokes don't create a negative sharpness
        }   
    }
    public char clearly(int myAction, int enemyAction) {
        if(myAction > 5) myAction = 5;
        if(enemyAction > 5) enemyAction = 5;
        return cleverman[myAction][enemyAction];
    }
}
class Caveman {
    private int sharpness;
    private int disposition;    //Finite State Machine: how inclined the caveman is toward blocking (0) or sharpening (4)
    public Caveman(char[] action) {
        sharpness = 0;
        disposition = 1;        //assume a slightly defensive disposition
        for (int i = 0; i < action.length; i++) {
            if(action[i] == 'S') {
                sharpness++;
                disposition++;
            }
            else if(action[i] == 'P') sharpness--;
            else disposition--;                     //blocking
            if(sharpness < 0) sharpness = 0; //ensure multiple pokes don't create a negative sharpness
            if(disposition > 4) disposition = 4;
            else if(disposition < 0) disposition = 0;
        }
    }
    public int getSharpness() {
        return sharpness;
    }
    public boolean weaponless() {
        return sharpness == 0;
    }
    public boolean hasSword() {
        return sharpness >= 5;
    }
    public boolean onEdge() {
        return sharpness == 4;
    }
    public boolean isDefensive() {
        return disposition == 0;
    }
    public boolean isSharpener() {
        return disposition == 4;
    }
    public int getDisposition() {
        return disposition;
    }
}

amwallis

Posted 2014-07-23T14:10:56.457

Reputation: 31

3

Deep Thoughts, C

Caveman code. Caveman think. Caveman do.

// DeepThoughts.c
#include <stdio.h>  // Me need for plan
#include <string.h> // Me need for memory

// Me count sharps. If me still here, pokes no work
int is_pointy(char *past){
    int pointy = 0;     // Stick dull
    while(*past){
        switch(*past ++){
            case 'S': pointy ++; break;
            case 'P': if(pointy > 0) pointy --;
        }
    }
    return pointy;
}

// Me brain
int main(int argc, char *argv[]){
    int me_pointy = 0;  // Is 0, stick dull. Is 5, has sword
    int you_pointy = 0; // Same to you
    int me_last;        // Me last plan
    int you_last;       // Same to you
    char *you;          // You past
    int when;           // Time
    int me_plan;        // Me deep thought

    // Me remember
    if(argc > 1){
        you = strchr(argv[1], ',');     // Me find you past in me arg
        *you ++ = 0;
        when = strlen(argv[1]);         // Time is passing
        me_pointy = is_pointy(argv[1]); // Me look at me past
        you_pointy = is_pointy(you);    // Same to you
        me_last = argv[1][when - 1];    // Why me do that?
        you_last = you[when - 1];       // Same to you
    }

    // Me has deep thoughts. Me make plan
    if(me_pointy >= 5) me_plan = 'P';       // Me has sword
    else if(you_pointy == 0) me_plan = 'S'; // Me safe. You stick dull
    else if(when == 1) me_plan = 'P';       // Me shoot first (more thought)
    else if(me_pointy == 1 && when < 42) me_plan = 'B';  // Me try for sharper (deeper thought)
    else if(me_pointy > 0) me_plan = 'P';   // Me stick not dull
    else if(me_last == 'P') me_plan = 'B';  // Me in trouble
    else me_plan = 'S';                     // Me cross toes

    // Me do plan
    putchar(me_plan);
    return 0;
}

Me do testing. More thoughts better.

Zakipu

Posted 2014-07-23T14:10:56.457

Reputation: 413

1+1 for the caveman var names and comments :P Also, nice program c: – cat – 2016-04-19T13:51:08.467

2

PokeBackBot

Simply adapted from PokeBot:

puts 'SBPB'[(ARGV.shift || ',').split(',', 2)[0].length % 4]

Run with ruby pokebackbot.rb.

This uses the next simplest strategy, and blocks "patiently" for one round before attacking.

Martin Ender

Posted 2014-07-23T14:10:56.457

Reputation: 162 549

3@PeterTaylor I read that as not being allowed to switch my strategy based on finger-printing the opponent. If my submission can only beat one other submission, that won't really affect that other submissions score, and my own submission will probably fare very badly. Furthermore, if there is only one submission, and a second one is written, that second one is likely to beat the first one (because otherwise, why bother) - does that alone qualify as "specific for another program"? My bot will beat any bot that starts with SPS (which seems reasonable), but so far PokeBot was the only around. – Martin Ender – 2014-07-23T15:24:27.350

2

Wooden Shield

This caveman's strength is like a wooden shield: strong enough to survive every poke with a stick, but not strong enough to survive a sword. It blocks in a lot of cases, it only sharpens if that's safe and it only pokes if it has a sword or it is likely that the opponent sharpens.

def getSharpness (history)
    sharpness = 0
    for i in 0..history.length - 1
        case history[i]
        when 'S', 's'
          sharpness += 1
        when 'P', 'p'
          sharpness -= 1
        end
    end
    return sharpness
end

if ARGV.length == 0
    puts 'S' # At the first run, sharpen.
else
    inputParts = ARGV[0].split(',')
    myHistory = inputParts[0]
    opponentHistory = inputParts[1]
    mySharpness = getSharpness(myHistory)
    opponentSharpness = getSharpness(opponentHistory)
    if mySharpness == 0 && opponentSharpness == 0
        puts 'S' # It's safe to sharpen now, the opponent cannot poke.
    elsif mySharpness > 0 && opponentSharpness == 0
        puts 'P' # Poke, the chance that the opponent sharps now is higher than when he has more sharpness.
    elsif mySharpness > 0 && opponentSharpness == 4
        puts 'P' # It is likely that the opponent sharpens now, because he wants a sword.
    elsif mySharpness > 4
        puts 'P' # Me win! (hopefully...)
    elsif mySharpness == 0 && opponentSharpness == 4
        puts 'S' # Uh oh... sharpen anyway, it is unlikely, but there *is* still a small chance that the opponent will sharpen once more after he got a sword.
    elsif mySharpness > 0 && opponentSharpness > 4
        puts 'P' # Poke, just in case the opponent sharpens.
    elsif mySharpness == 0 && opponentSharpness > 4
        puts 'S' # Sharpen anyway, for the same reasons as above. 
    else
        puts 'B' # In all other cases, block!
    end       
end

Run with ruby WoodenShield.rb.

ProgramFOX

Posted 2014-07-23T14:10:56.457

Reputation: 6 554

2

SwordLover

SwordLover loves swords! He tries to make a sword as fast as possible, then starts stabbing with it. Written in lua, run with lua SwordLover.lua

i=arg[1] or ""
if i:len() > 10 then
  print("P")
else
  print("S")
end

waylon531

Posted 2014-07-23T14:10:56.457

Reputation: 411

It's supposed to take command line arguments, not read standard input. (Also, there will be no arguments on the first turn.) I don't know how to fix that, so I'm going to have to exclude this from the first round of testing. – Doorknob – 2014-07-23T22:59:24.823

Okay, fixed to work on the first turn. – waylon531 – 2014-07-23T23:19:48.160

Alright, thanks! This submission is now included in the leaderboard. – Doorknob – 2014-07-24T03:04:35.327

2

Swordmaster

Written in Python 3.4 (works with Python 3.x)

Tries to get a sword as fast as possible but attacks if it has a chance to hit him (sharpness > 0) and enemy could hurt it too (enemy sharpness > 0).
Blocks only if has no sharpness and enemy could attack.

Start with:

python3 swordmaster.py MOVES

(assumed you save it as swordmaster.py)

Quick and ugly code:

import sys, random
dg = False
if len(sys.argv) > 1:
    ow,ot = sys.argv[1].split(',')
else:
    ow = ot = ""
def gs(m):
    ow = 0
    ot = 0
    i = 0
    ms = m[0]
    mo = m[1]
    for _ in mo:
        if ms[i] == 'S':
            ow += 1
        elif ms[i] == 'P' and mo[i] in ['P','B']:
            ow -= 1
        if mo[i] == 'S':
            ot += 1
        elif mo[i] == 'P' and ms[i] in ['P','B']:
            ot -= 1
        if dg:
            print("Own: {}, Other: {}".format(ow,ot))
        i += 1
    return [ow, ot]

def sm(sh):
    if (type(sh) != list) and dg:
        raise ValueError('Invalid sh type.')
    ow, ot = sh
    if ow >= 5:
        ret = 'P'
    elif ow >= 0 and ot == 0:
        ret = 'S'
    elif ow > 0 and ot > 0:
        ret = 'P'
    elif ow == 0 and ot > 0:
        ret = 'B'
    else:
        ret = random.choice(['S','B','P']) #Should not happen
    return ret

if __name__ == "__main__":
    print(sm(gs([ow,ot])))

(Set dg to True to enable debug messages)

chill0r

Posted 2014-07-23T14:10:56.457

Reputation: 161

1Hint: Don't let it battle itself - it'll deadlock with S,P,S,P... – chill0r – 2014-07-23T17:48:41.460

I found this happens with mine too. Unless you examine history or use a degree of randomness, you're bound to get stuck in a cycle. – Pharap – 2014-07-27T09:45:45.607

2

FoolMeOnce.py

Save each player's moves for the first duel, then replay with the exact same moves. If the enemy's algorithm is nonrandom, we can predict the same outcome and strike only when we know we'll win.

import os
import sys
import random

def getLastMove(player, turn):
    path = 'players/FoolMeOnce/'+player+str(turn)+'.txt'
    if os.path.isfile(path):
        with open(path, 'r') as f:
            return f.read()
    else:
        return 'nofile'

def sharpness(history):
    sharpness = 0
    for c in history:
        if c is 'S':
            sharpness+=1
        elif c is 'P' and sharpness > 0:
            sharpness-=1
    return sharpness

def takeTurn(choice, history, turn):
    print(choice)
    with open('players/FoolMeOnce/me'+str(turn)+'.txt', 'w') as f:
        f.write(choice)
    #also record their last choice
    choice = history[-1]
    with open('players/FoolMeOnce/them'+str(turn)+'.txt', 'w') as f:
        f.write(choice)

#if its the first turn, always sharpen
if(len(sys.argv) == 1):
    print('S')

else:
    history = sys.argv[1].split(',')
    meSharp = sharpness(history[0])
    themSharp = sharpness(history[1])
    turn = len(history[0])

    #read opponents move and our move for this turn from last duel
    them = getLastMove('them', turn);
    me = getLastMove('me', turn);

    #if this is first duel, fool me once
    if(them is 'nofile' or me is 'nofile'):
        if themSharp is 0 and meSharp >0:
            takeTurn(random.SystemRandom().choice('PS'), history, turn)
        else:
            takeTurn('B', history, turn)

    #if we could have played a winning move, do it. otherwise do what we did last time
    elif(them is 'S' and meSharp > 0):
        takeTurn('P', history, turn)
    else:
        takeTurn(me, history, turn)

Written in python 3, so most likely you'll have to use python3 FoolMeOnce.py On the first round, I'm not sure if we get an empty string or just a comma, so there may be some tweaks needed.

tzazy

Posted 2014-07-23T14:10:56.457

Reputation: 61

I've fixed your file path -- turns out the current working directory isn't your program's. – Doorknob – 2014-07-23T23:12:36.100

@Doorknob thanks! – tzazy – 2014-07-24T01:03:14.973

While playing with the CavemanDuel tester, I noticed that FoolMeOnce get way better points if I use more threads (I tested 16 threads against 4). With 4 threads it gets ~25 points, with 16 it gets ~34. – wendelbsilva – 2014-07-24T19:06:04.557

Weird, I have no idea why that would be. – tzazy – 2014-07-28T12:36:18.133

2

WantASword

Sharpens on the first turn. Tries to obtain a sword, throwing in a few blocks occasionally, and throws in a few rare pokes even without a sword. If enemy has a sword, then stab as often as possible.

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

void output(char p){
    putchar(p);
    exit(0);
}

int main(int argc, char **argv){
    srand(time(NULL) + 100);
    if(argc == 1) output('S');
    // get sharpness of self+opponent
    int my_sharp = 0, opp_sharp = 0;
    char *p = argv[1];
    while(*p != ','){
        if(*p == 'S') ++my_sharp;
        else if(*p == 'P') --my_sharp;
        ++p;
    }
    ++p;
    while(*p != 0){
        if(*p == 'S') ++opp_sharp;
        else if(*p == 'P') --opp_sharp;
        ++p;
    }
    if(opp_sharp == 0){
        if(my_sharp == 0) output('S');
        else if(rand() % 3) output('S');
        else output('P');
    }else if(opp_sharp < 5){
        if(my_sharp == 0){
            if(rand() % 2) output('S');
            else output('B');
        }else{
            if(rand() % 2) output('S');
            else if(rand() % 2) output('B');
            else output('P');
        }
    }else{
        if(my_sharp == 0) output('S');
        else output('P');
    }
    return 0xDEAD;
}

File: WantASword.c

Compile: gcc WantASword.c -o WantASword.out

Run: ./WantASword.out BBBBB,SSSSS...etc

IllogicalCaveman

Chooses moves randomly* if either player is at 0 sharpness. Otherwise, randomly* decides to either choose a predetermined move based on the previous move of itself or its opponent.

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

void output(char p){
    putchar(p);
    exit(0);
}

int main(int argc, char **argv){
    srand(time(NULL) + 0xF00D);
    if(argc == 1) output('S');
    // get sharpness of self+opponent
    int my_sharp = 0, opp_sharp = 0;
    char *p = argv[1], *q;
    while(*p != ','){
        if(*p == 'S') ++my_sharp;
        else if(*p == 'P') --my_sharp;
        ++p;
    }
    q = p++;
    while(*p != 0){
        if(*p == 'S') ++opp_sharp;
        else if(*p == 'P') --opp_sharp;
        ++p;
    }
    if(opp_sharp == 0){
        if(my_sharp == 0) output('S');
        else if(rand() % 10 < 6) output('P');
        else output('S');
    }else if(my_sharp == 0){
        if(rand() % 10 < 6) output('S');
        else output('B');
    }else{
        if(rand() % 10 < 6){
            switch((-1)[q]){
                case 'B': output('P');
                case 'P': output('S');
                case 'S': output('B');
            }
        }else{
            if((-1)[p] == (-2)[p]){
                switch((-1)[p]){
                    case 'S': output('P');
                    case 'B': output('S');
                    case 'P': output('B');
                }
            }else if(rand() % 10 < 6){
                output('P');
            }else output('B');
        }
    }
    return 0xDEAD;
}

File: IllogicalCaveman.c

Compile: gcc IllogicalCaveman.c -o IllogicalCaveman.out

Run: ./IllogicalCaveman.out BBBBB,SSSSS...etc

*5:3

es1024

Posted 2014-07-23T14:10:56.457

Reputation: 8 092

2

HuddleWolfWithStick - Java

HuddleWolf is back, but with a stick. In an environment that lacks anything to huddle with (since all cavemen are hostile), HuddleWolf has taken up the charge of bearing a stick. HuddleWolf, being of huddlish nature, takes the stick wherever he goes and sleeps with it close every night. Unbeknownst to our hero, however, is that the stick he is carrying is a long lost weapon from the Forgotten Times. Cursed with a will of it's own and power beyond imagination, The Stick whispers dark secrets into our dear HuddleWolf's ear. Seeking powers even darker than it's own, The Stick empowers HuddleWolf with a vicious intellect and a keen eye in an attempt to become the King of The Hill Cave.

import java.util.Random;

public class HuddleWolfWithStick { 

    public static void main(String[] args) {

        if (args.length == 0 || !args[0].contains(",")) {
            System.out.print("S");
            return;
        }

        String[] history = args[0].split(",");
        int mySharpness = getSharpness(history[0]);
        int enemySharpness = getSharpness(history[1]);
        Random gen = new Random(System.currentTimeMillis());

        // win game
        if (mySharpness >= 5) {
            System.out.print("P");
            return;
        }
        // time running out; can attack for remainder of game
        // spam poke for win or tie
        if (100 - history[0].length() <= mySharpness) {
            System.out.print("P");
            return;
        }
        // time running out; enemy cannot get sword before the end
        // spam block for tie
        if (100 - history[0].length() < (5 - enemySharpness)) {
            System.out.print("B");
            return;
        }
        // safe sharpen
        if (enemySharpness == 0 || isBlocker(history[1])) {
            System.out.print("S");
            return;
        }
        // cannot attack, so sharpen or block
        if (enemySharpness > 0 && mySharpness == 0) {
            int m = (gen.nextInt(2));
            switch(m) {
                case 0: System.out.println('B'); 
                    break;
                case 1: System.out.println('S'); 
                    break;
            }
            return;
        }
        // if tied, random move
        if (enemySharpness > 0 && mySharpness > 0 && enemySharpness == mySharpness) {
            int m = (gen.nextInt(3));
            switch(m) {
                case 0: System.out.println('B'); 
                    break;
                case 1: System.out.println('S'); 
                    break;
                case 2: System.out.println('P'); 
                    break;
            }
            return;
        }
        // if losing, play defensive (B - 40%, S - 20%, P - 40%)
        if (enemySharpness > 0 && mySharpness > 0 && enemySharpness > mySharpness) {
            int m = (gen.nextInt(5));
            switch(m) {
                case 0: 
                case 1: System.out.println('B'); 
                    break;
                case 2: System.out.println('S'); 
                    break;
                case 3: 
                case 4: System.out.println('P');
                    break;
            }
            return;
        }
        // if winning, go offensive(B - 20%, S - 40%, P - 40%)
        if (enemySharpness > 0 && mySharpness > 0 && enemySharpness < mySharpness) {
            int m = (gen.nextInt(5));
            switch(m) {
                case 0: System.out.println('B'); 
                    break;
                case 1:
                case 2: System.out.println('S'); 
                    break;
                case 3: 
                case 4: System.out.println('P');
                    break;
            }
            return;
        }
    }

    private static int getSharpness(String history) {
        int sharpness = 0;
        for (char move : history.toCharArray()) {
            if (move == 'S') {
                sharpness++;
            } 
            if (move == 'P' && sharpness > 0) {
                sharpness--;
            }
            if (move == 'P' && sharpness >= 5) {
                sharpness = 0;
            }
        }
        return sharpness;
    }

    private static boolean isBlocker(String history) {
        if (history.length() < 7) {
            return false;
        }
        for (int i = history.length() - 1; i > history.length() - 7; i--) {
            if (history.charAt(i) != 'B') {
                return false;
            }
        }
        return true;
    }
}

Compile : javac HuddleWolfWithStick.java

Run : java HuddleWolfWithStick

HuddleWolf

Posted 2014-07-23T14:10:56.457

Reputation: 223

2

SharpenBlockPoke

SharpenBlockPoke does exactly what you would think. He starts with sharpening his stick, followed by blocking and then poking. He repeats these three steps forever.

Run with:

python SharpenBlockPoke.py

Source:

import sys
args = sys.argv

if len(args) > 1:
    turns_played = (len(args[1]) - 1) / 2
else:
    turns_played = 0

print 'SBP'[turns_played % 3]

Tyilo

Posted 2014-07-23T14:10:56.457

Reputation: 1 342

2

Gruntt

Gruntt is defensive. Gruntt analyzes other cavemen moves to know how to poke them. Then he pokes them right in the eye. Gruntt is not a nice caveman.

public class Gruntt {

public static void main(String[] args) {
    System.out.println(whatToDo(args));
}

private static String whatToDo(String[] args){
    int mySharpness = 0;
    int otherSharpness = 0;

    if (args.length > 0) {
        String[] ss = args[0].split(",");
        mySharpness = howSpiky(ss[0]);
        otherSharpness = howSpiky(ss[1]);
    } else {
        return "S";
    }

    if (mySharpness >= 5){
        return "P";
    }

    String res = wowoo(args[0].split(",")[1]);
    if ("P".equals(res) && mySharpness > 0) {
        return "P";
    } else if ("P".equals(res) && mySharpness == 0) {
        return "S";
    } else if ("S".equals(res) && !args[0].split(",")[0].endsWith("S")) {
        return "S";
    }

    if (otherSharpness == 4 && !args[0].split(",")[0].endsWith("P")){
        return "P";
    }

    if (otherSharpness == 0){
        return "S";
    }

    return "B";

}

private static int howSpiky(String s1) {
    int count = 0;
    char[] c1 = s1.toCharArray();
    for (int i = 0; i < c1.length; i++) {
    if (c1[i] == 'S') {
            count++;
        } else if (c1[i] == 'P'){
            count --;
        }
    }
    return count;
}

private static String wowoo(String s){
    String s1 = "";
    String s2 = "";

    if (s.length() >= 4){
        s1 = s.substring(s.length() - 4);
    }

    if (s.length() >= 3){
        s2 = s.substring(s.length() - 3);
    }

    if ("SPSP".equals(s1)){
        return "P";
    } else if ("SSS".equals(s2)){
        return "P";
    } else if ("BBBB".equals(s1)){
        return "S";
    } else if ("SBSB".equals(s1)){
        return "P";
    }

    return null;
}

}

Compile with javac Gruntt.java and run with java Gruntt

Averroes

Posted 2014-07-23T14:10:56.457

Reputation: 2 298

This throws an ArrayOutOfBoundsException on the first turn, and it sometimes outputs multiple actions on other turns. – Doorknob – 2014-07-28T21:24:20.027

@Doorknob Ops! Fixed, thanks! – Averroes – 2014-07-29T06:15:32.307

2

bash-magnon

Bash-magnons were robustly built and powerful. The body was generally heavy and solid with a strong musculature. The forehead was fairly straight rather than sloping like in Neanderthals, and with only slight browridges. The face was short and wide. The chin was prominent. The brain capacity was about 1,600 cubic centimetres (98 cu in), larger than the average for modern humans. However, recent research suggests that the physical dimensions of so-called "Bash-Magnon" are not sufficiently different from modern humans to warrant a separate designation.

Me have a brain, me remember.

This is a self executable ./bash-magnon.sh

#!/bin/bash

function min () {
 [[ $1 -gt $2 ]] && echo $2 || echo $1
}

function max () {
[[ ${1%% *} -gt ${2%% *} ]] && echo $1 || echo $2
}

declare -A brain
declare -i C S P B me he
he=0
me=0
C=0
S=0; B=0; P=0

left=${1%%,*}
right=${1##*,}
while  : 
do

    [[ "${right:$C:1}" ]] && brain[$he$me]=${right:$C:1}
    case "${left:$C:1}${right:$C:1}" in
    BB) true;;
    BP) ((he--));;
    BS) ((he++));;
    PP) ((he--)); ((me--));;
    PB) ((me--));;
    PS|SP) exit;;
    SB) ((me++));;
    SS) ((me++)); ((he++));;
    "") break;;
    esac
    me=$(max 0 $me)
    me=$(min 9 $me)
    he=$(max 0 $he)
    he=$(min 9 $he)
    ((C++))
done

[[ $me$he =  *[5-9] ]] && ((P+=2))
[[ $me$he =  [5-9]* ]] && ((P+=2))
[[ $me$he =  [1-9]0 ]] && ((P+=2))
[[ $me$he =  00 ]] && ((S+=2))
[[ $me$he =  [1-4]4 ]] && ((P+=2))
[[ $me$he =  0[1-4] ]] && ((S+=1))
[[ $me$he =  0* ]] && ((B+=1))

case "${brain["$he$me"]}" in 
S) ((P+=2));;
B) ((S+=2));;
P) ((B+=2));;
*) ((B++));;
esac

set $(max "$B B" "$(max "$P P" "$S S")" )
echo $2

Emmanuel

Posted 2014-07-23T14:10:56.457

Reputation: 241

1+ You obviously have the right tool for the job and your cavemen names are quite amusing :) (I personally like fish better though) – Sylwester – 2014-07-30T22:52:38.940

@Sylwester Thank you that's my first +1. I tried first to make an homeostatic automata inspired by what did the first cybernetician something that feels its own balance, then I gave up and made a bash script. – Emmanuel – 2014-07-31T08:26:24.757

2

MultiMarkov

I meant for this caveman to use Markov chain analysis on both the current match and every match he has previously played. I got as far as writing the first half and ran a test against about half the bots (all that I have interpreters for on my local machine) and it came out on top. I figure it's worth submitting now, and improving later.

States are defined by the last N moves of each player, along with their current sharpnesses, so for N=2 there are 6*6*9*9=2916 states each with three outbound edges counting how frequently the opponent has chosen S/P/B from that state. N=3 leads to 9x as many states, too sparse for a single match, but probably useful for tracking hundreds or thousands of matches in the future.

The script evaluates the observed moves for the current state and for every "neighbor" state (different by one move or sharpness). Weighting between that information is rough so far.

Run with:

python -O players/MultiMarkov/MultiMarkov.py

Code:

#!/usr/bin/env python
import sys, itertools, collections

# how much history should we consider for the markov chain?
L = 2

def sharp(hist):
    return min(hist.count('S') - hist.count('P') , 5)

# takes a dict and two player's histories
# fills the dict with the opponent's next move,
# based on current sharpnesses and the last L moves of each player
def train(A, a_hist, b_hist):
    # keep track of the last L moves for each player
    a_past = a_hist[0:L]
    b_past = b_hist[0:L]
    if len(a_hist)<L+1:
        return
    a_shrp = sharp(a_past)
    b_shrp = sharp(b_past)
    a_hist = a_hist[L:]
    b_hist = b_hist[L:]
    # step through the string and track sharpness as we go
    while len(b_hist):
        b_m = b_hist[0]
        b_hist = b_hist[1:]
        if __debug__:
            print a_shrp, b_shrp, a_past, b_past, b_m
        A[a_shrp,b_shrp,a_past,b_past,b_m] += 1
        a_m = a_hist[0]
        a_hist = a_hist[1:]
        a_past = a_past[-(L-1):] + a_m
        b_past = b_past[-(L-1):] + b_m
        if b_m == 'P':
            b_shrp -= 1
        if b_m == 'S':
            b_shrp += 1
        if a_m == 'P':
            a_shrp -= 1
        if a_m == 'S':
            a_shrp += 1
    b_m = 'P'
    if __debug__:
        print a_shrp, b_shrp, a_past, b_past, b_m
    # assume that they won or will win with a poke
    # TODO: only assume when considering non-current matches
    # TODO: figure out if I won, instead
    A[a_shrp,b_shrp,a_past,b_past,b_m] += 1



# get the move history for each player
if len(sys.argv) > 1:
    pa, pb = sys.argv[1].split(',')
else:
    pa, pb = '', ''

# TODO: track and train based on all historical matches
# filedir='players/MultiMarkov'
# matchfiles = sorted([ f for f in os.listdir(filedir) if f.startswith('match')])
# if len(matchfiles) > 0:
#   candidate = matchfiles[-1]
#   with open(filedir + '/' + matchfiles[-1]) as cf:
#       turn = len(cf.readline())
#       if turn == len(pa): # newline included in turn count, not in pa.turn count
#           matchnum = int(candidate[5:])
#       else:
#           matchnum = int(candidate[5:])+1
# else:
#   matchnum = 0
# with open(filedir + '/match' + str(matchnum).rjust(20,'0'),'w+') as f:
#   f.write(pa + '\n')
#   f.write(pb + '\n')

# dict[pa sharpness, pb sharpness, pa last L moves, pb last L moves]
# { S:count, P:count, B:count }
# M will contain counts for every enemy faced so far combined
# N will contain counts for the current enemy
# M =
N = collections.Counter()

train(N, pa, pb)

a_shrp = sharp(pa)
b_shrp = sharp(pb)
# recent past for pa and pb
a_past = pa[-L:]
b_past = pb[-L:]
if __debug__:
    print a_shrp, b_shrp, a_past, b_past

# add up all the s,p,b seen as next moves
# for every scenario in the neighborhood of the current scenario
s = 0
p = 0
b = 0
# sharpness neighborhood is tricky, since 0 and 5 are special cases
for a_shrp_t in [[0],[1,2],[1,2,3],[2,3,4],[3,4],[5]][a_shrp]:
    for b_shrp_t in [[0],[1,2],[1,2,3],[2,3,4],[3,4],[5]][b_shrp]:
        # filtering of move neighborhood happens below
        for a_past_t in [''.join(cc) for cc in itertools.product('SPB',repeat=L)]:
            for b_past_t in [''.join(cc) for cc in itertools.product('SPB',repeat=L)]:
                # only consider move lists exactly one move different from the real situation
                if \
                    sum(c1 != c2 for c1,c2 in zip(a_past,a_past_t))==1 or \
                    sum(c1 != c2 for c1,c2 in zip(b_past,b_past_t))==1:
                    if __debug__:
                        print a_shrp_t,b_shrp_t,a_past_t,b_past_t,N[a_shrp_t,b_shrp_t,a_past_t,b_past_t,'S'], N[a_shrp_t,b_shrp_t,a_past_t,b_past_t,'P'], N[a_shrp_t,b_shrp_t,a_past_t,b_past_t,'B']
                    s += N[a_shrp_t,b_shrp_t,a_past_t,b_past_t,'S']
                    p += N[a_shrp_t,b_shrp_t,a_past_t,b_past_t,'P']
                    b += N[a_shrp_t,b_shrp_t,a_past_t,b_past_t,'B']

# now add the current scenario, with as much weight per outcome as all other scenarios combined
t = s+p+b
if __debug__:
    print a_shrp,b_shrp,a_past,b_past, \
        N[a_shrp,b_shrp,a_past,b_past,'S'], \
        N[a_shrp,b_shrp,a_past,b_past,'P'], \
        N[a_shrp,b_shrp,a_past,b_past,'B']
s += N[a_shrp,b_shrp,a_past,b_past,'S']*(t+1)
p += N[a_shrp,b_shrp,a_past,b_past,'P']*(t+1)
b += N[a_shrp,b_shrp,a_past,b_past,'B']*(t+1)
if __debug__:
    print 'S:' + str(s),'P:' + str(p),'B:' + str(b)

if (sharp(pa)>4):
    print 'P' # poke with a sword
elif (p >= s and p >= b and sharp(pb)>0):
    print 'B' # block an incoming poke
elif (s >= b and sharp(pa) > 0):
    print 'P' # poke if we expect them to sharpen
else:
    print 'S' # sharpen if we expect them to block

Sparr

Posted 2014-07-23T14:10:56.457

Reputation: 4 846

2

CaveMonkey (in Java)

I've implemented a form of simple DNA and executed an evolutionary algorithm (random warriors) for a while. This is what I've come up with:

    public class CaveMonkey {

    public static void main(String[] args) {
        System.out.println(new CaveMonkey().fight(args));;
    }

    private int[] used;
    private int[][] dna;

    public CaveMonkey() {
        this.dna = new int[][] { //dna is found by evolutionary algorithm and mating with neanderthalers
            {1, 1, 2, 0, 2, 2, 0, 1, 1, 2, 0, 0, 0, 1, 2, 0, 2, 2, 0, 2, 2, 1, 1, 2, 1, 0, 1, 2, 0, 0, 0, 2, 2, 1, 2, 0},
            {0, 1, 2, 2, 0, 2, 0, 1, 2, 1, 0, 2, 2, 1, 2, 1, 1, 0, 0, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 0, 0, 0, 2, 1, 2},
            {1, 0, 2, 0, 2, 2, 0, 1, 1, 0, 1, 2, 0, 2, 1, 2, 0, 2, 1, 1, 1, 0, 1, 0, 2, 2, 0, 0, 0, 0, 2, 1, 2, 0, 1, 0},
        };
        this.used = new int[dna[0].length];
    }

    private static final String[] I_DO = new String[] { "S", "B", "P" };
    private static final int[][] LOOKUP_TABLE = {
            { -1, -1, 0, 0, 0, 0, 0, 1, 1 }, // me
            { -1, 0, 0, -1, 0, 1, 0, 0, 1 }, // ugly you
    };

    public String fight(String[] input) {
        if (input.length == 0) {
            return "S";
        }
        String[] s = input[0].split(",");
        char[] me = s[0].toCharArray();
        char[] bleh = s[1].toCharArray();

        int stickMe = 0;
        int stickStupid = 0;
        for (int i = 0; i < me.length; i++) {
            // i can math!
            int code = ((((me[i] % 4) + (me[i] % 2)) / 2) * 3)
                    + (((bleh[i] % 4) + (bleh[i] % 2)) / 2);
            stickMe += LOOKUP_TABLE[0][code];
            stickStupid += LOOKUP_TABLE[1][code];
            int played = Math.min(5, stickMe) * 6 + Math.min(5, stickStupid);
            used[played]++;
        }

        stickMe = Math.max(0, stickMe);
        stickStupid = Math.max(0, stickStupid);

        int dnaLookup = Math.min(5, stickMe) * 6 + Math.min(5, stickStupid);
        String move = I_DO[dna[used[dnaLookup]%3][dnaLookup]];
        if(stickMe == 0 && move.equals("P")) {
            return "B";
        }
        return move;
    }
}

Compile: javac CaveMonkey.java

Run: java CaveMonkey

Roy van Rijn

Posted 2014-07-23T14:10:56.457

Reputation: 977

1

Sir Pokealot

Sir Pokealot was one of the Caveknights of the Stone Table. He served the Caveman Leader after witnessing him pull the legendary stick out of the rock. He betrayed the leader and now has a chance to bring down the tribe. He mainly bides his time and waits for poking windows.

"""Sir Pokealot"""

import sys, random

def makeMove(player, rival):
    next_move = 'B'
    if len(player.history) == 0:
        next_move = 'S'
        return next_move
    if player.inStalemate():
        if player.isHostile():
            next_move = 'P'
        else:
            next_move = 'S'
        return next_move        
    if rival.isHostile():
        next_move = 'B'
    if ((rival.isFatal() or rival.isNearFatal()) and player.isHostile()) or player.isFatal():
        next_move = 'P'
        return next_move
    if not rival.isHostile():
        if player.isHostile():
            if not player.isNearFatal():
                next_move = 'P'
            else:
                next_move = 'S'
        else:
            next_move = 'S'
        return next_move
    if rival.getPattern() == 'sharpener' and not rival.isFatal() and player.isHostile():
        next_move = 'P'
    elif rival.getPattern() == 'blocker' and not rival.isFatal():
        next_move = 'S'
    elif rival.getPattern() == 'poker' and not rival.isHostile() and player.isHostile():
        next_move = 'P'
    else:
        next_move = 'B'
    return next_move


class Caveman:
    def __init__(self):
        self.history = ''
        self.sharpness = 0
        self.blocks = 0
        self.pokes = 0

    def getStats(self):
        for move in self.history:
            if move == 'S':
                self.sharpness += 1
            elif move == 'P':
                self.pokes += 1
                self.sharpness -= 1
            else:
                self.blocks += 1

    def getPattern(self):
        if 1 < len(self.history) < 5:
            if self.sharpness == len(self.history):
                return 'sharpener'
            if self.blocks >= len(self.history)-1:
                return 'blocker'
            if self.pokes == self.sharpness or self.pokes == self.sharpness - 1:
                return 'poker'
        return 'smartypants'

    def isHostile(self):
        return self.sharpness > 0

    def isNearFatal(self):
        return self.sharpness == 4

    def isFatal(self):
        return self.sharpness > 4

    def inStalemate(self):
        return len(self.history) > 90

player = Caveman()
rival  = Caveman()

if len(sys.argv) > 1:
    player.history, rival.history = sys.argv[1].split(',')

player.getStats()
rival.getStats()

print(makeMove(player, rival))

Written in Python 3.4.1. File name: sirpokealot.py. Use python sirpokealot.py or python3 sirpokealot.py for the first run depending on the system. Use python sirpokealot.py PLAYERMOVES,RIVALMOVES for subsequent moves.

Tymric

Posted 2014-07-23T14:10:56.457

Reputation: 673

The command for the first run is python3 sirpokealot.py (with no arguments). I can't include this until that is fixed. – Doorknob – 2014-07-24T02:40:10.027

@Doorknob , Fixed – Tymric – 2014-07-24T06:26:30.270

1

Touché

Randomness guided by some heuristic rules that attempt to gauge the level of aggression shown by the opponent in order to determine what move to make next.

Run as:

$ perl touche.pl

Code (save as touche.pl):

use strict;
use warnings;
use List::Util 'sum';

sub random { $_[ rand @_ ] }

sub grunt { print $_[0]; exit 0 }

sub rounds { length $_[0] }

sub sharpness {
    my $combatant = shift;
    print $combatant, "\n";
    my $sharpens = () = $combatant =~ /S/ig;
    my $pokes    = () = $combatant =~ /P/ig;
    my $sharpness = $sharpens - $pokes;

    return   $sharpness < 0 ? 0
           : $sharpness > 5 ? 5
           : $sharpness
}

sub aggression {
    my ( $me, $he ) = map { rounds( $_ ) > 5 ? substr( $_, -5 ) : $_ } @_;

    my $score = 0;
    for ( 0 .. length $me ) {
        $score += substr( $me, $_, 1 ) eq 'B' && substr( $he, $_, 1 ) eq 'P';
        $score += substr( $me, $_, 1 ) eq 'S' && substr( $he, $_, 1 ) eq 'B';
        unless ( $score == 0 ) {
            $score -= substr( $me, $_, 1 ) eq 'B' && substr( $he, $_, 1 ) eq 'S';
            $score -= substr( $me, $_, 1 ) eq 'P' && substr( $he, $_, 1 ) eq 'B';
        }
    }

    return $score <=> 0;
}

sub touche { sharpness( $_[0] ) == 5 }

my ( $me , $he ) = @ARGV ? split ',' , shift : ( '', '' );
my %moves = (
    '-1' => [ 'P', 'S', 'P', 'S', 'P' ],
     '0' => [ 'P', 'S', 'P', 'S', 'P' ],
     '1' => [ 'P', 'P', 'S', 'P', 'P' ],
);

grunt 'S' if rounds( $me ) == 0;
grunt 'P' if rounds( $me ) == 1
            or touche( $me )
              or sharpness( $he ) == 4
                or sharpness( $me ) == 0 && sharpness( $he ) > 0;

grunt random( 'S', 'P' ) if sharpness( $me ) == 0;
grunt random( @{ $moves{ aggression( $me, $he ) || sharpness( $me ) <=> sharpness( $he ) } } );

Zaid

Posted 2014-07-23T14:10:56.457

Reputation: 1 005

Only blocks when its stick is dull? – Brilliand – 2014-07-24T15:43:49.463

1

MasterPoker

Follows some simple rules, but if none of these apply, the MasterPoker choses a random action, but ensures he doesn't use the same move 3 times in a row, to avoid other cavemen looking for a pattern.

Run with:

python MasterPoker.py

Code:

import sys
import random
args = sys.argv

if len(args) > 1:
    arg1 = args[1]
else:
    arg1 = ''

splitted = arg1.split(',')

if len(splitted) != 2:
    splitted = ['', '']

sword_sharpness = 5
max_turns = 100
turns_played = len(splitted[0])

players = []

for moves in splitted:
    player = {
        'sharpness': 0
    }
    players.append(player)

    for move in moves:
        if move == 'S':
            player['sharpness'] += 1
        elif move == 'P':
            player['sharpness'] -= 1

psharp = players[0]['sharpness']
osharp = players[1]['sharpness']

if psharp + turns_played >= max_turns:
    print 'P'
elif psharp >= sword_sharpness:
    print 'P'
elif psharp == sword_sharpness - 1 and osharp > 0:
    print 'B'
elif osharp == 0:
    print 'S'
elif osharp >= sword_sharpness:
    print 'S'
elif osharp == sword_sharpness - 1 and psharp > 0:
    print 'P'
else:
    options = set('S')
    if osharp > 0:
        options.add('B')

    if psharp > 0:
        options.add('P')

    if turns_played >= 2 and len(options) > 1:
        last_moves = splitted[0][-2:]
        if last_moves[0] == last_moves[1]:
            last = last_moves[0]
            if last in options:
                options.remove(last)

    print random.choice(list(options))

Tyilo

Posted 2014-07-23T14:10:56.457

Reputation: 1 342

1

MonteCarloMan know no strategies.
MonteCarloMan only know how to gamble
Last version just sharpened then poked everything else :-(

Save as filename: montecarloman.cpp
Run with montecarloman <input> where input is the "SSS,BBB" kind of string

#include <iostream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdlib>

using namespace std;
pair<int,int> next_state(int s_self,int s_enemy,int player_move,int enemy_move);
int simulation (int s_self,int s_enemy,int n_turn);

int to_int(char c)
{
    if (c=='S')
    {
       return 0;
    }
    else if (c=='P')
    {
         return 1;
    }
    else
    {
        return 2;
    }
}

int main(int argc,char * argv[])
{
    srand(time(0));
    int self=0;
    int enemy=0;
    int n_turn=0;
    //argc=2
    if (argc==1)
    {
       //New Game
       //printf("New Game :D\n");
    }
    else
    {
        //Process States
        string s(argv[1]);
        string s1,s2;
        for (int i=0; i<s.length(); i++)
        {
            if (s[i]==',')
            {
               s1=s.substr(0,i);
               s2=s.substr(i+1,i);
            }
        }
        for (int i=0; i<s1.length(); i++)
        {
            pair<int,int> p=next_state(self,enemy,to_int(s1[i]),to_int(s2[i]));
            self=p.first;
            enemy=p.second;
        }
        n_turn=s1.length();
    }
    //printf("self: %d,enemy: %d\n",self,enemy);
    int t_self,t_enemy,n_iter=1000,s_win=0,p_win=0,b_win=0;
    for (int i=0; i<n_iter; i++)
    {
        pair<int,int> p=next_state(self,enemy,0,rand()%3);
        s_win+=simulation(p.first,p.second,n_turn+1);
        p=next_state(self,enemy,1,rand()%3);
        p_win+=simulation(p.first,p.second,n_turn+1);
        p=next_state(self,enemy,2,rand()%3);
        b_win+=simulation(p.first,p.second,n_turn+1);
    }
    //printf("s_win: %d,p_win: %d,b_win: %d\n",s_win,p_win,b_win);
    if (s_win>=p_win && s_win>=b_win)
    {
       printf("S");
    }
    else if (p_win>=s_win && p_win>=b_win)
    {
         printf("P");
    }
    else
    {
        printf("B");
    }
    //system("PAUSE");
}

pair<int,int> next_state(int s_self,int s_enemy,int player_move,int enemy_move) //Playermove, enemymove
{
    int a=player_move;
    int b=enemy_move;
    if (a==0) //Sharpen
    {
             if (b==0)
             {
                return make_pair(s_self+1,s_enemy+1);
             }
             else if (b==1)
             {
                  if (s_enemy==0)
                  {
                     return make_pair(s_self+1,s_enemy);
                  }
                  else
                  {
                      return make_pair(-1,-1);
                  }
             }
             else
             {
                 return make_pair(s_self+1,s_enemy);
             }
    }
    else if (a==1) //Poke
    {
         if (b==0)
         {
            if (s_self>0)
            {
                return make_pair(9001,9001);
            }
            else
            {
                return make_pair(-1,-1);
            }
         }
         else if (b==1)
         {
            if (s_self>=5 && s_enemy<5)
            {
               return make_pair(9001,9001);
            }
            else if (s_self<=0)
            {
                return make_pair(-1,-1);
            }
            else
            {
                return make_pair(s_self-1,s_enemy-1);
            }

         }
         else if (b==2)
         {
              if (s_self>=5)
              {
                 return make_pair(9001,9001);
              }
              else if (s_self<=0)
                {
                    return make_pair(-1,-1);
                }
              else
              {
                  return make_pair(s_self-1,s_enemy);
              }
         }
    }
    else //Block
    {
        if (b==0)
        {
           return make_pair(s_self,s_enemy+1);
        }
        else if (b==1)
        {
             if (s_enemy>=5)
             {
                return make_pair(-1,-1);
             }
             else
             {
                 return make_pair(s_self,s_enemy-1);
             }
        }
        else
        {
            return make_pair(s_self,s_enemy);
        }
    }
}

int simulation (int s_self,int s_enemy,int n_turn)
{
    if (s_self==9001)
    {
       return 1;
    }
    else if (s_self==-1)
    {
         return 0;
    }
    int a=rand()%3;
    int b=rand()%3;
    if (n_turn>=100)
    {
       return 0;
    }
    if (a==0) //Sharpen
    {
             if (b==0)
             {
                return simulation(s_self+1,s_enemy+1,n_turn+1);
             }
             else if (b==1)
             {
                  if (s_enemy==0)
                  {
                     return simulation(s_self+1,s_enemy,n_turn+1);
                  }
                  else
                  {
                      return 0;
                  }
             }
             else
             {
                 return simulation(s_self+1,s_enemy,n_turn+1);
             }
    }
    else if (a==1) //Poke
    {
         if (b==0)
         {
            if (s_self<=0)
            {
                return 0;
            }
            else
            {
                return 1;
            }
         }
         else if (b==1)
         {
            if (s_self>=5 && s_enemy<5)
            {
               return 1;
            }
            else if (s_self<=0)
            {
                return 0;
            }
            else
            {
                return simulation(s_self-1,s_enemy-1,n_turn+1);
            }

         }
         else if (b==2)
         {
              if (s_self>=5)
              {
                 return 1;
              }
              else if (s_self<=0)
              {
                return 0;
              }
              else
              {
                  return simulation(s_self-1,s_enemy,n_turn+1);
              }
         }
    }
    else //Block
    {
        if (b==0)
        {
           return simulation(s_self,s_enemy+1,n_turn+1);
        }
        else if (b==1)
        {
             if (s_enemy>=5)
             {
                return 0;
             }
             else
             {
                 return simulation(s_self,s_enemy-1,n_turn+1);
             }
        }
        else
        {
            return simulation(s_self,s_enemy,n_turn+1);
        }
    }
}

guan

Posted 2014-07-23T14:10:56.457

Reputation: 11

Because of printf("self: %d,enemy: %d\n",self,enemy); this will block on every turn except the first. There are two comments. – Sylwester – 2014-07-30T23:32:36.287

1

The Semi-Patient Swordsmith - C

gcc -s -o semipatient semipatient.c

Works towards a sword very slowly, blocking most of the time. Will turn and strike if opponent tries to get a sword.

/* vi: ts=2
 * The Semi-Patient Swordsmith.
 * Caveman poke
 * Input: SPS,SBB (me,opp)
 */

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

#define ROUNDS 100
#define SWORD 5

unsigned statusof(char **ptr, unsigned *ctr)
{
    unsigned c = 0;
    *ctr = 0;
    while (**ptr && **ptr != ',')
            switch (*(*ptr)++) {
                    case 'S': c += 1; *ctr = 0; break;
                    case 'P': if (c) c -= 1; *ctr = 0; break;
                    case 'B': *ctr += 1; break;
            }
    return c;
}

int main(int argc, char **argv)
{
    unsigned me, opp;
    unsigned mbc, obc;
    unsigned rds;
    char act;
    if (!argv[0]) return 3;
    if (!argv[1] || !*argv[1]) argv[1] = ",";
    rds = strlen(argv[1]) >> 1;
    me = statusof(&argv[1], &mbc);
    ++argv[1];
    opp = statusof(&argv[1], &obc);

    if (me > ROUNDS - rds || me >= SWORD || opp >= SWORD)
            act = 'P';
    else if (!opp)
            if (rds % 5 > me)
                    act = 'P';
            else
                    act = 'S';
    else if (opp > 2 && SWORD - opp > rds % 5)
            act = 'P';
    else if (mbc > me + 1)
            act = 'S';
    else
            act = 'B';

    if (!me && act == 'P') act = 'S';
    if (rds == ROUNDS && act == 'S') act = 'B';
    putc(act, stdout);
    putc('\n', stdout);
}

Joshua

Posted 2014-07-23T14:10:56.457

Reputation: 2 281

What language is this, and how is it compiled and run? – Doorknob – 2014-07-26T03:20:16.873

1

MinimaxMan - Python 2

Caveman know game theory! Caveman use game theory to make decisions!

Run with python whateveryoucalledthefile

import random
import sys

strats = [[[  1.00000000e+00,   1.11022302e-16,  -1.11022302e-16],
        [  4.74869011e-01,   2.62565494e-01,   2.62565494e-01],
        [  3.95185387e-01,   3.02407307e-01,   3.02407307e-01],
        [  4.90423936e-01,   2.54788032e-01,   2.54788032e-01],
        [  3.33333333e-01,   3.33333333e-01,   3.33333333e-01],
        [  3.33333333e-01,   3.33333333e-01,   3.33333333e-01]],

       [[  6.94392031e-01,   3.05607969e-01,  -3.84545826e-16],
        [  2.79811766e-01,   1.44797649e-01,   5.75390584e-01],
        [  1.99394905e-01,   1.08811576e-01,   6.91793519e-01],
        [  1.98054806e-01,   1.11223463e-01,   6.90721731e-01],
        [  2.86589042e-01,   1.61422615e-01,   5.51988344e-01],
        [  3.33333333e-01,   3.33333333e-01,   3.33333333e-01]],

       [[  5.67410596e-01,   4.32589404e-01,  -3.03961924e-17],
        [  2.43101693e-01,   2.09166107e-01,   5.47732200e-01],
        [  1.87468915e-01,   1.28365828e-01,   6.84165257e-01],
        [  1.62197637e-01,   1.54319887e-01,   6.83482475e-01],
        [  2.45891641e-01,   2.51703841e-01,   5.02404519e-01],
        [  3.33333333e-01,   3.33333333e-01,   3.33333333e-01]],

       [[  4.85507685e-01,   5.14492315e-01,  -3.98442437e-17],
        [  2.04223408e-01,   2.44727502e-01,   5.51049090e-01],
        [  1.55739413e-01,   1.47879796e-01,   6.96380790e-01],
        [  1.25758285e-01,   1.86641625e-01,   6.87600090e-01],
        [  2.17930711e-01,   3.44297688e-01,   4.37771601e-01],
        [  3.33333333e-01,   3.33333333e-01,   3.33333333e-01]],

       [[  1.00000000e+00,  -3.17454396e-16,  -4.16333634e-17],
        [  1.54548065e-01,   2.19780954e-01,   6.25670981e-01],
        [  9.71025047e-02,   1.60591219e-01,   7.42306277e-01],
        [  8.38176056e-02,   1.96432741e-01,   7.19749653e-01],
        [  1.23705522e-01,   4.30154964e-01,   4.46139515e-01],
        [  3.33333333e-01,   3.33333333e-01,   3.33333333e-01]],

       [[  0.00000000e+00,   1.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   1.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   1.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   1.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   1.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   1.00000000e+00,   0.00000000e+00]]]

if len(sys.argv) > 1:
    me_moves, he_moves = sys.argv[1].split(',')
else:
    # Round 1, no input
    me_moves = he_moves = ''

me_sharp = me_moves.count('S') - me_moves.count('P')
he_sharp = he_moves.count('S') - he_moves.count('P')

# Table doesn't go past 5.
he_sharp = min(he_sharp, 5)

strat = strats[me_sharp][he_sharp]
randnum = random.random()

if randnum < strat[0]:
    print 'S'
elif randnum < strat[0] + strat[1]:
    print 'P'
else:
    print 'B'

This isn't quite the minimax strategy. Actually, I'm not even confident this is anything close. I might not have gotten all the bugs out of the strategy generator, and this is only the strategy table for round 1 (with some manual adjustments), since the full 100-round strategy table was too huge. I hope it works.

user2357112

Posted 2014-07-23T14:10:56.457

Reputation: 887

It doesn't seem to sharpen first turn. I think it's because you're adding 1 to the sharpness for some reason. – Brilliand – 2014-07-26T00:13:31.073

@Brilliand: Whoops, I thought sharpness started at 1. Fixing... and it should be fixed. Thanks. – user2357112 – 2014-07-26T00:14:22.537

It also tries to poke sometimes when at sharpness 0 - that'd be a bug in your strategy generator. The Nash equilibrium for this game might be exactly what Unpredictable Caveman does, though. – Brilliand – 2014-07-26T00:24:34.947

@Brilliand: 0-sharpness poking is because it treats a 0-sharpness poke as a block. Is that not how the game works? It should be easy enough to change. While we're talking, do you know how 0-sharpness pokes are handled in the move history? Do they become Bs, stay Ps, or something else? – user2357112 – 2014-07-26T00:29:38.400

Yes, actually Ps are converted to Bs in the move history - they're treated as invalid output, and anything invalid is converted to B. – Brilliand – 2014-07-26T01:05:19.693

1

The Entertainer

Python 3 caveman.

Caveman have party. Caveman poke guests. Caveman have plan.

from sys import argv
print("PSSBSPPBSB"[len(argv)<2 or (len(argv[1])+argv[1].count("B"))%10])

The name comes from his strange ability to influence the opponent's action.

cjfaure

Posted 2014-07-23T14:10:56.457

Reputation: 4 143

1

Oracle - Python2.x

Run command: python Oracle.py (use Python 2.X)

Code:

from sys import argv

class caveman():
    actions = ''
    sharpness = 0

    def __init__(self, actions):
        self.actions = actions
        for action in self.actions:
            if action is 'S':
                self.sharpness = self.sharpness + 1
            elif action is 'P':
                self.sharpness = self.sharpness - 1

history = ['','']
if len(argv) > 1:
    history = argv[1].split(',')

me = caveman(history[0])
he = caveman(history[1])

def print_and_exit(text):
    print text
    exit()

def try_sharpen():
    if he.sharpness is 0:
        if me.sharpness >= 5:
            print_and_exit('P')
        print_and_exit('S')

def find_in_history():
    sharpness = 0
    fpoints = []
    index = 0
    for action in he.actions:
        if sharpness is 0:
            fpoints.append(index)
        if action is 'S':
            sharpness = sharpness + 1
        elif action is 'P':
            sharpness = sharpness - 1
        index = index + 1
    if len(fpoints) > 1:
        move = he.actions[fpoints[-1]:]
        for fpoint in fpoints[:-1]:         
            if he.actions[fpoint:fpoint+len(move)] == move:
                if he.actions[fpoint+len(move)] is 'P':
                    if he.sharpness >= 5:
                        if me.sharpness > 0:
                            print_and_exit('P')
                        else:
                            print_and_exit('S')
                    elif he.sharpness is 4 and me.sharpness >= 5:
                        print_and_exit('P')
                    else:
                        print_and_exit('B')
                elif he.actions[fpoint+len(move)] is 'S':
                    if me.sharpness > 0:
                        print_and_exit('P')
                    else:
                        print_and_exit('S')
                elif he.actions[fpoint+len(move)] is 'B':
                    if me.sharpness >= 5:
                        print_and_exit('P')
                    else:
                        print_and_exit('S')

def try_poke():
    if me.sharpness > 0:
        print_and_exit('P')

def copy():
    if len(he.actions) > 0 and he.actions[-1] in ['S','B']:
        print_and_exit(he.actions[-1])

try_sharpen()
find_in_history()
try_poke()
copy()
print_and_exit('S')

Bob

Posted 2014-07-23T14:10:56.457

Reputation: 11

0

The Blocker

The blocker likes to block. If he sees a threat, he immediately blocks. If he gets bored from the slow fight, he'll get sirius for sure.

Run with node TheBlocker.js

function sharpenStick()
{
    meDoesWhat = 'S';
}

function block()
{
    meDoesWhat = 'B';
}

function poke()
{
    meDoesWhat = 'P';
}

if (process.argv.length > 2)
{
    cavemenDidWhat = process.argv[2].split(',');

    meMoves = cavemenDidWhat[0];
    mePokes = meMoves.split('P').length - 1;
    meSharpens = meMoves.split('S').length - 1;
    meHasSword = meSharpens - mePokes >= 5;

    opponentMoves = cavemenDidWhat[1];
    opponentPokes = opponentMoves.split('P').length - 1;
    opponentSharpens = opponentMoves.split('S').length - 1;
    opponentHasSword = opponentSharpens - opponentPokes >= 5;

    chooseMove();
}else
{
    sharpenStick();
}

function chooseMove()
{
    if (opponentSharpens - opponentPokes > 0 && meMoves < 50)
    {
        block();
    }else
    {
         var attackChance =  Math.random() * (opponentSharpens - 5);
         var isDoingFirst = Math.random() < .42;

         if (opponentHasSword)
         {
             attackChance -= 2;
         }

         if (attackChance > 2)
         {
             block();
         }else
         {
             if (meHasSword)
             {
                 poke();
             }else
             {
                 if (meSharpens > 0)
                 {
                     if (isDoingFirst)
                     {
                         poke();
                     }else
                     {
                         sharpenStick();
                     }
                 }else
                 {
                     if (isDoingFirst || meSharpens === 0)
                     {
                         block();
                     }else
                     {
                         sharpenStick();
                     }
                 }
             }
         }
     }
}

console.log(meDoesWhat);

Cilan

Posted 2014-07-23T14:10:56.457

Reputation: 489

0

Sharp Man

Let's see if we get a sword. Or get somebody to think we want a sword.

File SharpMan.java

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

public class SharpMan {
    public static void main(String[] args) {
        String arg = args != null && args.length > 0 ? args[0] : null;
        char action = action(arg);
        System.out.print(action);
    }

    protected static char action(String arg) {
        if (arg != null) {
            String[] split = arg.split(",");
            final int me = sharpness(split[0]);
            final int notMe = sharpness(split[1]);
            if (me > 4) {
                return 'P';
            } else if (me > 3) {
                return Math.random() >= 0.75 ? 'S' : 'P';
            } else if (notMe == 0 || notMe > 4) {
                return 'S';
            } else if (notMe == 1) {
                Matcher matcher = Pattern.compile("B+$").matcher(split[1]);
                if (matcher.find() && matcher.group().length() > new Random().nextInt(42) + 5) {
                    return 'S';
                }
                return 'B';
            } else if (notMe == 2) {
                return Math.random() >= 0.75 ? 'S' : 'B';
            } else if (notMe == 3) {
                return Math.random() >= 0.5 ? 'S' : (me < 3) ? 'P' : 'B';
            } else {
                return Math.random() >= 0.75 ? 'S' : 'P';
            }
        } else {
            return 'S';
        }
    }

    protected static int sharpness(String s) {
        int sharp = 0;
        for (char c : s.toCharArray()) {
            switch (c) {
                case 'P':
                    sharp = Math.max(0, sharp - 1);
                    break;
                case 'B':
                    break;
                case 'S':
                    sharp++;
                    break;
            }
        }
        return sharp;
    }
}

Compile: javac SharpMan.java Run: java SharpMan

TheConstructor

Posted 2014-07-23T14:10:56.457

Reputation: 553

0

ModestCaveman - C#

If you would prefer a C++ or Lua implementation, please tell me. If any exceptions are thrown, that's my program telling you that the input provided was not in the format described.

using System;
using System.Collections.Generic;

namespace ModestCaveman
{
    class Program
    {
        static char DecideNextMove(char[] playerHistory, char[] opponentHistory)
        {
            int playerSharpness = 0, opponentSharpness = 0;

            for (int i = 0; i < playerHistory.Length; i++)
            {
                switch (opponentHistory[i])
                {
                    case 'P': if (playerHistory[i] == 'P') playerSharpness--; break;
                    case 'B': if (playerHistory[i] == 'P') playerSharpness--; break;
                    case 'S': opponentSharpness++; break;
                    default: throw new ArgumentException(string.Format("Invalid input symbol provided: symbol {0}", opponentHistory[i]));
                }
                switch (playerHistory[i])
                {
                    case 'P': if (opponentHistory[i] == 'P') opponentSharpness--; break;
                    case 'B': if (opponentHistory[i] == 'P') opponentSharpness--; break;
                    case 'S': playerSharpness++; break;
                    default: throw new ArgumentException(string.Format("Invalid input symbol provided: symbol {0}", playerHistory[i]));
                }
            }

            if (opponentSharpness < playerSharpness) return 'P';
            if (opponentSharpness > playerSharpness) return 'B';
            if (opponentSharpness == 0) return 'S';
            if (opponentSharpness == playerSharpness) return 'P';
            return ((new Random().Next() & 1) == 1) ? 'B' : 'S';
        }

        static void Main(string[] args)
        {
            if (args.Length < 1) { Console.Write('S'); return; }

            string[] input = args[0].Split(',');

            if (input.Length < 2)
                throw new ArgumentException("Malformatted arugment. Argument expected to be in form \"(S|P|B),(S|P|B)\"");

            char[]
                playerHistory = input[0].ToCharArray(),
                opponentHistory = input[1].ToCharArray();

            if (playerHistory.Length != opponentHistory.Length)
                throw new ArgumentException("Number of player moves and number of opponent moves are not equal");

            Console.Write(DecideNextMove(playerHistory, opponentHistory));
        }
    }
}

Pharap

Posted 2014-07-23T14:10:56.457

Reputation: 196

Your Caveman throws System.IndexOutOfRangeException on the first round. – Sylwester – 2014-07-29T00:16:20.200

@Sylwester Probably just fixed it. If it throws an exception again, take note of the line number, it helps to speed up debugging. – Pharap – 2014-07-30T07:28:02.040

0

Haskell – CarefulBot

It definitely doesn't win in the benchmarks, but seeing as it's at least respectable performance I thought I'd post it. This bot/caveman plays as carefully as I could think to have it: it only attacks when it has a sword, or it is inevitable that its opponent will acquire a sword. Compile with ghc CarefulBot.hs (mind the capitalization in the executable and folder names—the bot relies on them).

Code:

import System.Environment
import Data.Maybe
import Data.List

main :: IO ()
main = do
  args <- getArgs
  writeFile attackFile "F"
  let (me, he) = if not (null args) then processInput (head args) else ("", "")
      meSharpness = sharpness me
      heSharpness = sharpness he
  determineMove heSharpness meSharpness

attackFile :: FilePath
attackFile = "players/CarefulBot/attack.txt"

processInput :: String -> (String, String)
processInput input = splitAt (fromJust $ elemIndex ',' input) input 

sharpness :: String -> Int
sharpness moves = length (elemIndices 'S' moves) - length (elemIndices 'P' moves)

determineMove :: Int -> Int -> IO ()
determineMove heSharpness meSharpness =
  case (heSharpness, meSharpness) of (0, 5) -> attack
                                     (0, 0) -> unAttack
                                     (0, _) -> checkAttack
                                     (4, x) -> if x > 0 && x < 4 then attack else putChar 'S'
                                     (_, _) -> putChar 'B'

attack :: IO ()
attack = do
  writeFile attackFile "T"
  putChar 'P'

checkAttack :: IO ()
checkAttack = do
  file <- readFile attackFile
  putChar $ case file of "T" -> 'P'
                         "F" -> 'S'
                         _   -> 'B'

unAttack :: IO ()
unAttack = do
  writeFile attackFile "F"
  putChar 'S'

DrJPepper

Posted 2014-07-23T14:10:56.457

Reputation: 364

What language is this? – Doorknob – 2014-07-28T21:42:59.230

I'm so sorry about that, this is Haskell. The ghc I meantioned in my post is the Glasgow Haskell Compiler, which is what you need to run the above. – DrJPepper – 2014-07-28T23:41:28.247

0

NakedEarlyNerd - PHP

I used ViceLeader and a dummy random bot to tune this one. He beats both 2 times out of 3.

A central idea was to remain relatively random, to defeat probabilistic or pattern-based predictors.

I also noticed that blocking was more often than not a losing option, so this caveman fights naked.

No idea how it will fare against the other contenders, though.

<?php
    // me see how sharp the sticks
    class History {
        public function __construct ($str)
        {
            $this->sharpness = 0;
            for ($i = 0 ; $i != strlen ($str) ; $i++)
            {
                switch ($str{$i})
                {
                    case 'S' : $this->sharpness++; break;
                    case 'P' : if ($this->sharpness > 0) $this->sharpness--; break;
                }
            }
        }
    }

    // me get headache
    function think ($own, $foe)
    {
        // me no want no blunt stick
        if ($own->sharpness == 0)                   return "S";

        // maybe he blocks me, maybe he blocks me not
        if ($foe->sharpness == 0)                   return "SP";

        // good stick, me rather poke
        if ($own->sharpness - $foe->sharpness > 1)  return "SPPP";

        // not so good stick, me rather sharpen
                                                    return "SSSP";
    }

    // me go and fight
    $hist = count ($argv) == 2 ? $argv[1] : ',';
    list ($own, $foe) = explode (',', $hist);
    $answer = str_shuffle(think(new History ($own), new History ($foe)));
    echo $answer{0};
    ?>

run it with

php NakedEarlyNerd.php <history>

user16991

Posted 2014-07-23T14:10:56.457

Reputation:

Based on Nash's percentages, blocking is actually the most favorable move most of the time. PokeBot in particular is extremely good at punishing players who don't block enough. However, it looks like your bot will fare well against bots that block too much, which most of the top-ranked bots do. – Brilliand – 2014-07-29T19:35:31.527

That's what I meant by "more often than not". The strategy is based on the actual behaviour of the current leaders, and I'm just betting the majority of the current contestants are not campers. Besides, Nash equilibrium is of little help when a single blow decides the outcome of the game and only 3 matches are played. – None – 2014-07-29T19:41:22.723

0

Monkey and Elephant - Fantom

I'm a little late on this one, but I've got a couple worth a shot.

//Monkey See Monkey Do...
class Monkey
{

  static void main(String[] args){
    Monkey m = new Monkey();
    m.play(args[0]);
  }

  public String play(String args){

    //Always sharpen first, duh
    if(args.length() < 3){ sharpen(); }
    else{
      String him = args.split(",")[1];
      char hisLast = him.toCharArray()[him.toCharArray().length - 1];
      switch(hisLast){
        case 'P': return poke();
        case 'S': return sharpen();
        case 'B': return block();
      }  
    }
    return sharpen();
  }

  static String sharpen(){ System.out.println("S"); return "S"; }
  static String block(){System.out.println("B"); return "B"; }
  static String poke(){ System.out.println("P"); return "P"; }





}

And this one, which basically just tries to guess the next move by the previous patterns

//Elephants never forget
class Elephant
{

  static Choice[] analysis = new Choice[9];

public Elephant(){ analysis[0] = new Choice("SS", 0); analysis[1] = new Choice("SB", 0); analysis[2] = new Choice("SP", 0); analysis[3] = new Choice("PS", 0); analysis[4] = new Choice("PB", 0); analysis[5] = new Choice("PP", 0); analysis[6] = new Choice("BS", 0); analysis[7] = new Choice("BB", 0); analysis[8] = new Choice("BP", 0); }

  static void main(String[] args){
    Elephant e = new Elephant();
    e.play(args[0]);
  }

    public String play(String args){

    //Always sharpen() first, duh
    if(args.length() < 3){ return sharpen(); }
    else{
      String him = args.split(",")[1];
      String me = args.split(",")[0];
      analyze(him);
      int hisSharp = findSharp(him);
      int meSharp = findSharp(me);

      //What will he do next?
      char hisLast = him.toCharArray()[him.toCharArray().length - 1];
      Choice[] hisOptions = new Choice[3];
      int k = 0;
      for(int i = 0; i < 9; i++){
          if(analysis[i].pattern.toCharArray()[0] == hisLast){
              hisOptions[k++] = analysis[i];
          }
      }


      char hisNext = 'P';
      int mostLikely = 0;
      for(Choice c : hisOptions){
          if(c.probability > mostLikely){ mostLikely = c.probability; hisNext = c.pattern.toCharArray()[1]; }
      }




      //Now go through potential cases
      System.out.println("His Next = $hisNext");
      System.out.println("Me Sharp = $meSharp");
      //If me have sword, swing it
      if(meSharp > 4){ return poke(); }
      else if(meSharp > 0){
        switch(hisNext){
          case 'P': return block();
          case 'B': return sharpen();
          case 'S': return poke();
        }
      }
      else{
        switch(hisNext){
          case 'P': return block();
          case 'B': return block();
          case 'S': return sharpen();
        }
      }
    }
    return block();
  }

  static String sharpen(){ System.out.println("S"); return "S"; }
  static String block(){System.out.println("B"); return "B"; }
  static String poke(){ System.out.println("P"); return "P"; }


  void analyze(String his){
    char[] shortString = his.substring(1,-1).toCharArray();
    char[] longString = his.toCharArray();
    for(int i = 0; i < shortString.length; i++) {
      String pattern = "" + shortString[i] + longString[i];
      for(Choice c : analysis){
          if(c.pattern == pattern){
              c.probability++;
          }
      }
    }
  }

  int findSharp(String hist){
      int sharp = 0;
      for(char c : hist.toCharArray()){
          if(c == 'S'){ sharp++; }
          else if (c == 'P'){ sharp--;}
      }
      return sharp;
  }

}

class Choice{
    String pattern;
    int probability;

    Choice(String p, int i){
        pattern = p;
        probability = i;
    }
}

Standard java running, build with javac Monkey.java run with java Monkey

Cain

Posted 2014-07-23T14:10:56.457

Reputation: 609

For unix users there is a script under adm called unixsetup that seems to fix the fan binary. I cannot get Monkey working though since apparently the standard library doesn't have a class called CaveMan. Elephant also has an error which goes away when I remove override though it prints out debug before move which makes this play "B" all the time :( – Sylwester – 2014-07-30T21:16:04.187

@Sylwester I'm too new to Fantom to know where that debug statement is coming from, so I just went ahead and ported to java, not too hard. Thanks for the debugging! – Cain – 2014-07-30T22:34:55.130

There are still issues with your programs after you converted them to Java. If the files you test without arguments and with S,S you should consider replacing the whole code instead of just pieces. – Sylwester – 2014-08-01T19:46:18.277

These didn't successfully compile for me. – Doorknob – 2014-08-03T00:21:27.050

0

Blind Fury, Java

Blind, angry, and under-evolved

public class BlindFury {

    public static void main(String[] args) {
            if(args.length == 0 || !args[0].contains(",")) {
                System.out.print("S");
                return;
            }

            String[] moves = args[0].split(",");
            int sharp = getSharp(moves[0]);
            int rageMeter = getRage(moves[0], moves[1]);


            if(rageMeter >= 0){
                //Urge to kill rising
                if(sharp > 0) {
                    System.out.println('P');
                    return;
                }
                else {
                    System.out.println('S');
                    return;
                }
            }
            else{
                //Not enough rage, lame it out.
                System.out.print('B');
                return;
            }
        }

       private static int getSharp(String mine) {
        int sharp = 0;

        for(char c : mine.toCharArray()) {
            if(c == 'S') {
                sharp++;
            }

            if(c == 'P') {
                sharp--;
            }
        }

        return sharp;
    }

 private static int getRage(String mine, String theirs) {
        int rage = 0;

        for(char c : mine.toCharArray()) {
            if(c == 'S') {
                rage++;
            }

            if(c == 'P') {
                rage--;
            }
        }

        for(char c : theirs.toCharArray()) {
            if(c == 'S') {
                rage--;
            }

            if(c == 'P') {
                rage++;
            }

            if(c == 'B') {
                rage+=2;
            }
        }

        return rage;
    }
}

Compile: javac BlindFury.java

Run: java BlindFury

Josef E.

Posted 2014-07-23T14:10:56.457

Reputation: 131

This doesn't compile unless you make all your methods static. – Sylwester – 2014-07-30T19:37:45.507

@Sylwester Oops, sorry about that. :) Edited – Josef E. – 2014-07-30T19:38:47.930

0

Krick - Java

This is Watson's java clone Krick. Uses a lot of the same logic with various upgrades along the way.

Compile: javac Krick.java Run: java Krick

Filename: Krick.java

import java.util.Arrays;
import java.util.Date;
import java.util.Random;

public class Krick {

    public static final long SEED = new Date().getTime();

    public static Random r = new Random(SEED);

    public static void main(String[] args){

        if(args.length == 0){
            System.out.print('S');
            return;
        }

        String[] history = args[0].split(",");

        int[] phistory = convertHistory(history[0]);
        int[] ohistory = convertHistory(history[1]);

        int[] psh = getSharpnessHistory(history[0]);
        int[] osh = getSharpnessHistory(history[1]);

        int turnNum = history[0].length();

        int[] nph = new int[16 + phistory.length];
        int[] noh = new int[16 + ohistory.length];

        Arrays.fill(nph, 3);
        Arrays.fill(noh, 3);

        for(int i = 0; i < phistory.length; i++){
            nph[i + 16] = phistory[i];
            noh[i + 16] = ohistory[i];
        }

        int[] nps = new int[16 + psh.length];
        int[] nos = new int[16 + osh.length];

        Arrays.fill(nps, 0);
        Arrays.fill(nos, 0);

        for(int i = 0; i < psh.length; i++){
            nps[i + 16] = psh[i];
            nos[i + 16] = osh[i];
        }

        if(getVal(nos, -10) - turnNum / 15 + 1 + turnNum % 3 - getVal(nos, -6) < 0 || getVal(nos, -6) + 1 - getVal(nos, -2) < 0){
            makeMove(0,0,100);
        }else if((getVal(noh, -3) - getVal(nph, -1) <= 0 && getVal(nps, -1) - turnNum / 10 <= 0) || getVal(nos, -1) == 0){
            makeMove(0,100,0);
        }else{
            makeMove(100,0,0);
        }
    }

    //Makes a move from the provided values
    public static void makeMove(int block, int sharpen, int poke){
        double randVal = r.nextDouble() * 100;
        System.out.print("BSP".charAt((randVal >= block ? 1 : 0) + (randVal >= block + sharpen ? 1 : 0)));
    }

    public static int getVal(int[] array, int negativeIndexValue){
        return array[array.length - Math.abs(negativeIndexValue)];
    }

    //Calculate the sharpness from the provided history String
    public static int getSharpness(String history){
        return numTimes('S', history) - numTimes('P', history);
    }

    //Return the sharpness at the end of each turn
    public static int[] getSharpnessHistory(String history){
        int[] sharpnessHistory = new int[history.length()];
        for(int i = 0; i < history.length(); i++){
            sharpnessHistory[i] = getSharpness(history.substring(0, i + 1));
        }
        return sharpnessHistory;
    }

    //Return the number of times c appears in s
    public static int numTimes(char c, String s){
        return s.length() - s.replace(String.valueOf(c), "").length();
    }

    //Return numerical representation of the character
    public static int getNumeric(char c){
        return "BSP".indexOf(c);
    }

    //Convert text history to numerical
    public static int[] convertHistory(String history){
        int[] converted = new int[history.length()];
        for(int i = 0; i < history.length(); i++){
            converted[i] = getNumeric(history.charAt(i));
        }
        return converted;
    }
}

One of the scoreboards from my tests:

Score   Player
151     Krick
147     SpeculativeSylwester
147     Watson
141     Gruntt
128     BashMagnon
127     CaveDoctor
126     PrisonRules
124     MultiMarkov
120     ViceLeader
119     FancyTechnoAlgorithm
118     ChargerMan
115     Semipatient
114     Hodor
111     Watcher
109     RegExMan
99      Basilisk
97      Sicillian
97      Oracle
96      MaybeMarkov
96      JavaMan
94      SharpMan
93      Nigel
93      Nash
92      Entertainer
92      CarefulBot
90      MinimaxMan
90      Feint
86      BobCaves
85      SirPokealot
85      IllogicalCaveman
83      SSBBP
81      CaveMonkey
79      MasterPoker
77      HuddleWolfWithStick
75      Darwin
74      SharpenBlockPoke
71      Unpredictable
70      WoodenShield
65      PokeBackBot
64      PatientWolf
62      MonteCarloMan
61      PatientBlacksmith
58      CavekidBlocks
56      NakedEarlyNerd
56      Blocker
52      PokeBot
51      BlindFury
51      BinaryCaveman
49      LatePokeBot
48      Swordmaster
47      ModestCaveman
46      ForeignCaveman
41      WantASword
39      SwordLover
30      Trickster
25      FoolMeOnce
22      PeriodicalCavemanCicada
21      Aichmophobic
20      Touche
*(this leaderboard was auto-magically generated)*

The top robots (including Watson and Krick) seem to move around in the top 5 places randomnly each test.

spocot

Posted 2014-07-23T14:10:56.457

Reputation: 366

Watson find DNA and no tell Krick. Krick angry. Krick poke. – spocot – 2014-08-05T14:12:00.160

It's the bots that use randomness that moves the results around. Some of the dumbest bots can win against any of the top 5 by chance and since the duel does only 3 rounds each you'll have variance. Thats why I made Studious and even if I can't fight random I beat everything that isn't. – Sylwester – 2014-08-07T22:08:03.153

@Sylwester Yeah, that was my guess as well, however every one of my tests places your bot @ 147 points which is quite interesting indeed. – spocot – 2014-08-08T16:37:41.800

Speculative yes, but not Studious. Studious does 151-161 on first and better on consecutive rounds. – Sylwester – 2014-08-08T17:40:00.837

@Sylwester I haven't actually tested against Studious yet, I just grabbed the bots that were on GH. – spocot – 2014-08-08T17:41:47.293

0

Wordsmith

Other cavemen think the Wordsmith is too intellectual and out-of-touch with caveman politics, but really he just wants to get back to writing his novel. He looks for patterns in his enemy's behavior and tries to exploit them by poking at just the right moment.

import collections
from math import *
import random
import sys


## Logic


def act():
    '''Determine an action to take!'''

    my_hist, his_hist = action_histories()

    # Hard-code the first few turns:
    if not my_hist:
        sharpen()

    if len(my_hist) <= 3:
        block()

    # Once I have a little data, I can try to pick an intelligent action.

    predicted = predict_move(his_hist)

    if sharpness(my_hist) >= 2:  # Can attack (never drop to zero sharpness)
        if predicted == POKE:
            if sharpness(his_hist) >= 5:
                poke()
            else:
                block()
        elif predicted == BLOCK:
            sharpen()
        else:
            poke()
    else:
        if predicted == POKE:
            block()
        else:
            sharpen()


def predict_move(hist):
    '''Attempt to predict the enemy action using a probability function.'''

    if not hist:
        return sharpen

    if sharpness(hist) >= 5:
        return POKE

    def seq_weight(seq):
        '''Calculate the weight to assign to a sequence.'''

        return log(len(seq) + e)

    def sharp_weight(s):
        '''Calculate the weight to assign to a sharpness.'''

        return ((s+1)**1.2)/3

    sharp_hist = sharpness_history(hist)

    seq_probs = collections.defaultdict(lambda: {a:0.01 for a in range(3)})
    sharp_probs = collections.defaultdict(lambda: {a:0.01 for a in range(3)})

    for seq_len in range(1, len(hist) + 1):
        for n in range(len(hist)):
            seq = hist[n:n + seq_len]
            if len(seq) != seq_len:
                continue

            action = seq.pop(-1)
            seq = tuple(seq)
            sharp = sharp_hist[n + seq_len - 1]

            qw = seq_weight(seq)
            seq_probs[seq][action] += qw

            sw = sharp_weight(sharp)
            sharp_probs[sharp][action] += sw

    if sharpness(hist) == 0:
        for probs in seq_probs.values():
            del probs[POKE]
        for probs in sharp_probs.values():
            del probs[POKE]

    pool = []
    for seq_len in range(len(hist) + 1):
        seq = tuple(hist[len(hist) - seq_len:])
        if seq in seq_probs:
            for action in seq_probs[seq]:
                pool.extend([action] * int(seq_probs[seq][action]*10000))
    danger = sharpness(hist)
    if danger in sharp_probs:
        for action in sharp_probs[danger]:
            pool.extend([action] * int(sharp_probs[danger][action]*10000))

    return random.choice(pool)


## Utilities


BLOCK, SHARPEN, POKE = range(3)


def action_histories():
    '''Get my_hist, his_hist from stdin.'''

    if len(sys.argv) > 1:
        mine, his = sys.argv[1].split(',')
        return ['BSP'.index(a) for a in mine], ['BSP'.index(a) for a in his]
    return [], []


def sharpness(hist):
    '''Calculate a sharpness for a given history.'''

    return max(hist.count(SHARPEN) - hist.count(POKE), 0)


def sharpness_history(hist):
    '''Calculate sharpness at each step along the way.'''

    return [0] + [sharpness(hist[:i+1]) for i in range(len(hist))]


def block():
    '''Block an incoming attack.'''

    sys.stdout.write(random.choice(synonyms[BLOCK]))
    exit()


def sharpen():
    '''Sharpen my stick.'''

    sys.stdout.write(random.choice(synonyms[SHARPEN]))
    exit()


def poke():
    '''Poke with my stick!'''

    sys.stdout.write(random.choice(synonyms[POKE]))
    exit()


## Silliness


synonyms = {
    BLOCK: ['BLOCK', 'BEEF UP', 'BARRICADE', 'BRACE', 'BULWARK', 'BACK', 'BAR',
            'BUFFER', 'BOTTLENECK'],
    SHARPEN: ['SHARPEN', 'STRAP', 'STROP', 'SET UP', 'SCARE', 'SET'],
    POKE: ['POKE', 'PUNCH', 'PULVERIZE', 'POUND', 'PUNISH', 'PUGILIZE', 'PRICK',
           'PIKE', 'POUNCE', 'PENETRATE', 'PELT', 'PICK', 'PAROXYSM', 'PIN']
}

act()

Run with python3 wordsmith.py (though technically the Wordsmith is backwards-compatible as far back as Python 2.7).

Henry Keiter

Posted 2014-07-23T14:10:56.457

Reputation: 131

0

UghThenUgh - Python 3

Save as ughthenugh.py and run with python3.3 ughthenugh.py [histories].

UghThenUgh is more advanced than any generic caveman. He's at least 1.1 times smarter than them!
Here is the code:

import sys
if (len(sys.argv) >= 2):
   strings = sys.argv[1].split(',')
   my_history = list(strings[0])
   your_history = list(strings[1])
   hist_len = len(my_history) # Both histories should be the same. If they aren't, let me know.
else:
   hist_len = 0
your_sharp = 1
my_sharp = 1
# Generate sharpness of stick data
if hist_len > 0:
 for count in range(0, hist_len):
   if (my_history[count] == 'P' and your_history[count] == 'P'):
     my_sharp = my_sharp - 1
     your_sharp = your_sharp - 1
   if (my_history[count] == 'P' and your_history[count] == 'B'):
     my_sharp = my_sharp - 1
   if (my_history[count] == 'B' and your_history[count] == 'P'):
     your_sharp = your_sharp - 1 
   if (my_history[count] == 'S'):
     my_sharp = my_sharp + 1
   if (your_history[count] == 'S'):
     your_sharp = your_sharp + 1

# Now, give caveman sharp stick order
if (hist_len >= 90):
 order = 'P'
elif (your_sharp <= 0 and my_sharp > 0):
 order = 'P'
elif (your_sharp <= 0 and my_sharp <= 0):
 order = 'S'
elif (my_sharp > 0 and your_sharp > 0):
 order = 'B'
else:
 order = 'B'

print(order)

ASCIIThenANSI

Posted 2014-07-23T14:10:56.457

Reputation: 1 677

0

CavemanBotWithAPointyStick (Javascript)

var input=prompt("").split(",");
function what_me_do(){
if(input.length<2)return "S";
var me=input[0];
var otherguy=input[1];
var mesharp=(me.match(/S/g)||[]).length-(me.match(/P/g)||[]).length;
var othersharp=(otherguy.match(/S/g)||[]).length-(otherguy.match(/P/g)||[]).length;
if(mesharp>=5)return "P";
if(othersharp==0&&mesharp>0)return "P";
if(Math.random()>0.4||othersharp==0)return "S";
if(Math.random()>0.5&&othersharp>0)return "B";
if(mesharp==0)return "S";
return "P"
}
alert(what_me_do());

I posted it to JS Fiddle to make the testing part easier.

SuperJedi224

Posted 2014-07-23T14:10:56.457

Reputation: 9 433

I think what you're asking is if JavaScript is allowed (from what I see, the program has nothing wrong with it.) Also, just for formatting purposes, I suggest making the name a header (by putting === in a blank line below the title) and adding your program's language next to it. – ASCIIThenANSI – 2015-04-09T19:11:16.137

0

Historical Bot

Me cavemen me think that those who do not learn from history are doomed to repeat it!

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package caveman;

import java.util.Scanner;

/**
 *
 * @author rohan
 */
public class Caveman {
        static int turns = 0;
        static char CavemanMove = ' ';
        static int humanSharpness = 0;
        static int caveSharpness = 0;
        static String pastHumanMoves="";
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
try{    
String input =args[0];
}catch(Exception ex){
input=",";
}
String[] inputs = input.split(",");
String me=inputs[0];
pastHumanMoves=inputs[1];
for(char c:me.toCharArray()){
    if(c=='S'){
        caveSharpness++;
    }
    if(c=='P'){
        if(caveSharpness>0){
            caveSharpness++;
        }
    }
}
for(char c:pastHumanMoves.toCharArray()){
    if(c=='S'){
        humanSharpness++;
    }
    if(c=='P'){
        if(caveSharpness>0){
            humanSharpness++;
        }
    }
}
turns=pastHumanMoves.length();
            System.out.println(CavemanMove = getBasedMove());


    }

    private static char getMove() {
//plays it fairly cautiously unless it knows something
if(turns<=1){
    return 'S';
}

if(turns==2){
    return Math.random()>.5?'P':Math.random()>.4?'S':'B';
}
if(Math.random()<.1){
    int i=(int) (Math.random()*3);
    switch(i){
        case 0:
            return 'B';
        case 1:
            return 'S';
        case 2: 
            return 'P';
    }
}
if(caveSharpness>5){
return 'P';
}
if(humanSharpness==0){
    return Math.random()<.85?'S':'P';
}
if(humanSharpness>=3&&caveSharpness==0){
return Math.random()>.7?'B':'S';
}
if(humanSharpness>=3&&caveSharpness>0){
    return Math.random()>.4?'P':'B';
}

if(caveSharpness>0){
    if(Math.random()>.2){
        return 'P';
    }
}
return Math.random()>.3?'B':'S';
    }
/**
    * Looks at the past to predict the future
    */
    private static char getBasedMove() {
int length=6;
        if(turns<length){
    return getMove();
}
int pokes=0;
int sharpenings=0;
int blocks=0;

if(length>pastHumanMoves.length()){
    length=pastHumanMoves.length();
}
for(int i=pastHumanMoves.length()-1;i>=0&&i>pastHumanMoves.length()-length;i--){
            if(pastHumanMoves.charAt(i)=='P'){
                pokes++;
            }else if(pastHumanMoves.charAt(i)=='S'){
                sharpenings++;
            }else {
                blocks++;
            }
        }
if(turns<=1){
    return 'S';
}
if(caveSharpness>=5){
return 'P';
}
if(humanSharpness==0){
    return Math.random()>sharpenings/length?'S':'P';
}
if(humanSharpness>=3&&caveSharpness==0){
return Math.random()>(pokes+humanSharpness)/length?'S':'B';
}
if(humanSharpness>=3&&caveSharpness>0){
return Math.random()< (humanSharpness+sharpenings-1)/length?'P':'B';
}

if(caveSharpness>0){
    if(Math.random()>.3&&Math.random()<(1+sharpenings)/length){
        return 'P';
    }else if(Math.random()<pokes/length){
    return 'B';
}else{
        return 'S';
    }
}
if(caveSharpness==0){
    return Math.random()<(sharpenings+blocks+1)/length?'S':'B';
}
return 'B';
    }
}

run as java Caveman.java

Rohan Jhunjhunwala

Posted 2014-07-23T14:10:56.457

Reputation: 1 878

You left in the comment template at the top xD – cat – 2016-04-19T13:51:38.643

I can't comment on anything? Is this still a challenge thats open? – Rohan Jhunjhunwala – 2016-04-22T00:48:41.790

I guess I can only comment on my posts – Rohan Jhunjhunwala – 2016-04-22T00:48:55.663