154
\$\begingroup\$

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

\$\endgroup\$
32
  • \$\begingroup\$ I'm surprised no one seems to have attempted to find the minimax strategy yet. It seems like the obvious thing to do. \$\endgroup\$ Jul 24, 2014 at 15:47
  • \$\begingroup\$ @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.) \$\endgroup\$
    – HuddleWolf
    Jul 24, 2014 at 18:32
  • 3
    \$\begingroup\$ Many 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? \$\endgroup\$
    – Sparr
    Jul 26, 2014 at 14:24
  • 6
    \$\begingroup\$ This needs to be here: dresdencodak.com/comics/2009-09-22-caveman_science_fiction.jpg Maybe it will get the imagination going. :) \$\endgroup\$ Jul 28, 2014 at 1:10
  • 2
    \$\begingroup\$ Is this still open? I see people adding new submissions, but I don't see the leaderboard being updated. \$\endgroup\$ Apr 10, 2015 at 16:36

57 Answers 57

1
2
2
\$\begingroup\$

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
\$\endgroup\$
3
  • \$\begingroup\$ 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. \$\endgroup\$
    – Doorknob
    Jul 23, 2014 at 22:59
  • \$\begingroup\$ Okay, fixed to work on the first turn. \$\endgroup\$
    – waylon531
    Jul 23, 2014 at 23:19
  • \$\begingroup\$ Alright, thanks! This submission is now included in the leaderboard. \$\endgroup\$
    – Doorknob
    Jul 24, 2014 at 3:04
2
\$\begingroup\$

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

\$\endgroup\$
2
\$\begingroup\$

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

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.

\$\endgroup\$
0
2
\$\begingroup\$

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

\$\endgroup\$
2
\$\begingroup\$

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

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

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ The command for the first run is python3 sirpokealot.py (with no arguments). I can't include this until that is fixed. \$\endgroup\$
    – Doorknob
    Jul 24, 2014 at 2:40
  • \$\begingroup\$ @Doorknob , Fixed \$\endgroup\$
    – Tymric
    Jul 24, 2014 at 6:26
1
\$\begingroup\$

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 ) } } );
\$\endgroup\$
1
  • \$\begingroup\$ Only blocks when its stick is dull? \$\endgroup\$
    – Brilliand
    Jul 24, 2014 at 15:43
1
\$\begingroup\$

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

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.

\$\endgroup\$
5
  • \$\begingroup\$ It doesn't seem to sharpen first turn. I think it's because you're adding 1 to the sharpness for some reason. \$\endgroup\$
    – Brilliand
    Jul 26, 2014 at 0:13
  • \$\begingroup\$ @Brilliand: Whoops, I thought sharpness started at 1. Fixing... and it should be fixed. Thanks. \$\endgroup\$ Jul 26, 2014 at 0:14
  • \$\begingroup\$ 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. \$\endgroup\$
    – Brilliand
    Jul 26, 2014 at 0:24
  • \$\begingroup\$ @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? \$\endgroup\$ Jul 26, 2014 at 0:29
  • \$\begingroup\$ Yes, actually Ps are converted to Bs in the move history - they're treated as invalid output, and anything invalid is converted to B. \$\endgroup\$
    – Brilliand
    Jul 26, 2014 at 1:05
1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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);
}
\$\endgroup\$
1
  • \$\begingroup\$ What language is this, and how is it compiled and run? \$\endgroup\$
    – Doorknob
    Jul 26, 2014 at 3:20
1
\$\begingroup\$

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')
\$\endgroup\$
1
\$\begingroup\$

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);
        }
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Because of printf("self: %d,enemy: %d\n",self,enemy); this will block on every turn except the first. There are two comments. \$\endgroup\$
    – Sylwester
    Jul 30, 2014 at 23:32
1
\$\begingroup\$

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

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

\$\endgroup\$
3
  • \$\begingroup\$ You left in the comment template at the top xD \$\endgroup\$
    – cat
    Apr 19, 2016 at 13:51
  • \$\begingroup\$ I can't comment on anything? Is this still a challenge thats open? \$\endgroup\$ Apr 22, 2016 at 0:48
  • \$\begingroup\$ I guess I can only comment on my posts \$\endgroup\$ Apr 22, 2016 at 0:48
0
\$\begingroup\$

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

\$\endgroup\$
0
\$\begingroup\$

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'
\$\endgroup\$
2
  • \$\begingroup\$ What language is this? \$\endgroup\$
    – Doorknob
    Jul 28, 2014 at 21:42
  • \$\begingroup\$ 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. \$\endgroup\$
    – DrJPepper
    Jul 28, 2014 at 23:41
0
\$\begingroup\$

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));
        }
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Your Caveman throws System.IndexOutOfRangeException on the first round. \$\endgroup\$
    – Sylwester
    Jul 29, 2014 at 0:16
  • \$\begingroup\$ @Sylwester Probably just fixed it. If it throws an exception again, take note of the line number, it helps to speed up debugging. \$\endgroup\$
    – Pharap
    Jul 30, 2014 at 7:28
0
\$\begingroup\$

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

\$\endgroup\$
2
  • \$\begingroup\$ This doesn't compile unless you make all your methods static. \$\endgroup\$
    – Sylwester
    Jul 30, 2014 at 19:37
  • \$\begingroup\$ @Sylwester Oops, sorry about that. :) Edited \$\endgroup\$
    – Josef E.
    Jul 30, 2014 at 19:38
0
\$\begingroup\$

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

\$\endgroup\$
4
  • \$\begingroup\$ 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 :( \$\endgroup\$
    – Sylwester
    Jul 30, 2014 at 21:16
  • \$\begingroup\$ @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! \$\endgroup\$
    – Cain
    Jul 30, 2014 at 22:34
  • \$\begingroup\$ 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. \$\endgroup\$
    – Sylwester
    Aug 1, 2014 at 19:46
  • \$\begingroup\$ These didn't successfully compile for me. \$\endgroup\$
    – Doorknob
    Aug 3, 2014 at 0:21
0
\$\begingroup\$

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>
\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$
    – Brilliand
    Jul 29, 2014 at 19:35
  • \$\begingroup\$ 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. \$\endgroup\$
    – user16991
    Jul 29, 2014 at 19:41
0
\$\begingroup\$

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.

\$\endgroup\$
5
  • \$\begingroup\$ Watson find DNA and no tell Krick. Krick angry. Krick poke. \$\endgroup\$
    – spocot
    Aug 5, 2014 at 14:12
  • \$\begingroup\$ 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. \$\endgroup\$
    – Sylwester
    Aug 7, 2014 at 22:08
  • \$\begingroup\$ @Sylwester Yeah, that was my guess as well, however every one of my tests places your bot @ 147 points which is quite interesting indeed. \$\endgroup\$
    – spocot
    Aug 8, 2014 at 16:37
  • \$\begingroup\$ Speculative yes, but not Studious. Studious does 151-161 on first and better on consecutive rounds. \$\endgroup\$
    – Sylwester
    Aug 8, 2014 at 17:40
  • \$\begingroup\$ @Sylwester I haven't actually tested against Studious yet, I just grabbed the bots that were on GH. \$\endgroup\$
    – spocot
    Aug 8, 2014 at 17:41
0
\$\begingroup\$

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

\$\endgroup\$
0
\$\begingroup\$

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);
\$\endgroup\$
0
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$ Apr 9, 2015 at 19:11
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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