Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Most Recent Leaderboard @ 2014-08-02 12:00

| Pos # | Author               | Name                    | Language   | Score | Win   | Draw  | Loss  | Avg. Dec. Time |
+-------+----------------------+-------------------------+------------+-------+-------+-------+-------+----------------+
| 1st   | Emil                 | Pony                    | Python2    | 064   | 064   | 000   | 005   | 0026.87 ms     |
| 2nd   | Roy van Rijn         | Gazzr                   | Java       | 062   | 062   | 001   | 006   | 0067.30 ms     |
| 2nd   | Emil                 | Dienstag                | Python2    | 062   | 062   | 001   | 006   | 0022.19 ms     |
| 4th   | ovenror              | TobiasFuenke            | Python2    | 061   | 061   | 001   | 007   | 0026.89 ms     |
| 5th   | PhiNotPi             | BayesianBot             | Perl       | 060   | 060   | 000   | 009   | 0009.27 ms     |
| 6th   | Claudiu              | SuperMarkov             | Python2    | 058   | 058   | 001   | 010   | 0026.77 ms     |
| 7th   | histocrat            | Alternator              | Ruby       | 057   | 057   | 001   | 011   | 0038.53 ms     |
| 8th   | histocrat            | LeonardShelby           | Ruby       | 053   | 053   | 000   | 016   | 0038.55 ms     |
| 9th   | Stretch Maniac       | SmarterBot              | Java       | 051   | 051   | 002   | 016   | 0070.02 ms     |
| 9th   | Martin Büttner       | Markov                  | Ruby       | 051   | 051   | 003   | 015   | 0038.45 ms     |
| 11th  | histocrat            | BartBot                 | Ruby       | 049   | 049   | 001   | 019   | 0038.54 ms     |
| 11th  | kaine                | ExcitingishBot          | Java       | 049   | 049   | 001   | 019   | 0065.87 ms     |
| 13th  | Thaylon              | UniformBot              | Ruby       | 047   | 047   | 001   | 021   | 0038.61 ms     |
| 14th  | Carlos Martinez      | EasyGame                | Java       | 046   | 046   | 002   | 021   | 0066.44 ms     |
| 15th  | Stretch Maniac       | SmartBot                | Java       | 045   | 045   | 001   | 023   | 0068.65 ms     |
| 16th  | Docopoper            | RoboticOboeBotOboeTuner | Python2    | 044   | 044   | 000   | 025   | 0156.55 ms     |
| 17th  | Qwix                 | Analyst                 | Java       | 043   | 043   | 001   | 025   | 0069.06 ms     |
| 18th  | histocrat            | Analogizer              | Ruby       | 042   | 042   | 000   | 027   | 0038.58 ms     |
| 18th  | Thaylon              | Naan                    | Ruby       | 042   | 042   | 004   | 023   | 0038.48 ms     |
| 20th  | Thaylon              | NitPicker               | Ruby       | 041   | 041   | 000   | 028   | 0046.21 ms     |
| 20th  | bitpwner             | AlgorithmBot            | Python2    | 041   | 041   | 001   | 027   | 0025.34 ms     |
| 22nd  | histocrat            | WereVulcan              | Ruby       | 040   | 040   | 003   | 026   | 0038.41 ms     |
| 22nd  | Ourous               | QQ                      | Cobra      | 040   | 040   | 003   | 026   | 0089.33 ms     |
| 24th  | Stranjyr             | RelaxedBot              | Python2    | 039   | 039   | 001   | 029   | 0025.40 ms     |
| 25th  | JoshDM               | SelfLoathingBot         | Java       | 038   | 038   | 001   | 030   | 0068.75 ms     |
| 25th  | Ourous               | Q                       | Cobra      | 038   | 038   | 001   | 030   | 0094.04 ms     |
| 25th  | Ourous               | DejaQ                   | Cobra      | 038   | 038   | 001   | 030   | 0078.31 ms     |
| 28th  | Luis Mars            | Botzinga                | Java       | 037   | 037   | 002   | 030   | 0066.36 ms     |
| 29th  | kaine                | BoringBot               | Java       | 035   | 035   | 000   | 034   | 0066.16 ms     |
| 29th  | Docopoper            | OboeBeater              | Python2    | 035   | 035   | 002   | 032   | 0021.92 ms     |
| 29th  | Thaylon              | NaanViolence            | Ruby       | 035   | 035   | 003   | 031   | 0038.46 ms     |
| 32nd  | Martin Büttner       | SlowLizard              | Ruby       | 034   | 034   | 004   | 031   | 0038.32 ms     |
| 33rd  | Kyle Kanos           | ViolentBot              | Python3    | 033   | 033   | 001   | 035   | 0032.42 ms     |
| 34th  | HuddleWolf           | HuddleWolfTheConqueror  | .NET       | 032   | 032   | 001   | 036   | 0029.86 ms     |
| 34th  | Milo                 | DogeBotv2               | Java       | 032   | 032   | 000   | 037   | 0066.74 ms     |
| 34th  | Timmy                | DynamicBot              | Python3    | 032   | 032   | 001   | 036   | 0036.81 ms     |
| 34th  | mccannf              | YAARBot                 | JS         | 032   | 032   | 002   | 035   | 0100.12 ms     |
| 38th  | Stranjyr             | ToddlerProof            | Java       | 031   | 031   | 010   | 028   | 0066.10 ms     |
| 38th  | NonFunctional User2..| IHaveNoIdeaWhatImDoing  | Lisp       | 031   | 031   | 002   | 036   | 0036.26 ms     |
| 38th  | john smith           | RAMBOBot                | PHP        | 031   | 031   | 002   | 036   | 0014.53 ms     |
| 41st  | EoinC                | SimpleRandomBot         | .NET       | 030   | 030   | 005   | 034   | 0015.68 ms     |
| 41st  | Martin Büttner       | FairBot                 | Ruby       | 030   | 030   | 006   | 033   | 0038.23 ms     |
| 41st  | Docopoper            | OboeOboeBeater          | Python2    | 030   | 030   | 006   | 033   | 0021.93 ms     |
| 44th  | undergroundmonorail  | TheGamblersBrother      | Python2    | 029   | 029   | 000   | 040   | 0025.55 ms     |
| 45th  | DrJPepper            | MonadBot                | Haskel     | 028   | 028   | 002   | 039   | 0008.23 ms     |
| 46th  | Josef E.             | OneBehind               | Java       | 027   | 027   | 007   | 035   | 0065.87 ms     |
| 47th  | Ourous               | GitGudBot               | Cobra      | 025   | 025   | 001   | 043   | 0053.35 ms     |
| 48th  | ProgramFOX           | Echo                    | .NET       | 024   | 024   | 004   | 041   | 0014.81 ms     |
| 48th  | JoshDM               | SelfHatingBot           | Java       | 024   | 024   | 005   | 040   | 0068.88 ms     |
| 48th  | Trimsty              | Herpetologist           | Python3    | 024   | 024   | 002   | 043   | 0036.93 ms     |
| 51st  | Milo                 | DogeBot                 | Java       | 022   | 022   | 001   | 046   | 0067.86 ms     |
| 51st  | William Barbosa      | StarWarsFan             | Ruby       | 022   | 022   | 002   | 045   | 0038.48 ms     |
| 51st  | Martin Büttner       | ConservativeBot         | Ruby       | 022   | 022   | 001   | 046   | 0038.25 ms     |
| 51st  | killmous             | MAWBRBot                | Perl       | 022   | 022   | 000   | 047   | 0016.30 ms     |
| 55th  | Mikey Mouse          | LizardsRule             | .NET       | 020   | 020   | 007   | 042   | 0015.10 ms     |
| 55th  | ja72                 | BlindForesight          | .NET       | 020   | 020   | 001   | 048   | 0024.05 ms     |
| 57th  | robotik              | Evolver                 | Lua        | 019   | 019   | 001   | 049   | 0008.19 ms     |
| 58th  | Kyle Kanos           | LexicographicBot        | Python3    | 018   | 018   | 003   | 048   | 0036.93 ms     |
| 58th  | William Barbosa      | BarneyStinson           | Lua        | 018   | 018   | 005   | 046   | 0005.11 ms     |
| 60th  | Dr R Dizzle          | BartSimpson             | Ruby       | 017   | 017   | 001   | 051   | 0038.22 ms     |
| 60th  | jmite                | IocainePowder           | Ruby       | 017   | 017   | 003   | 049   | 0038.50 ms     |
| 60th  | ArcticanAudio        | SpockOrRock             | PHP        | 017   | 017   | 001   | 051   | 0014.19 ms     |
| 60th  | Dr R Dizzle          | BetterLisaSimpson       | Ruby       | 017   | 017   | 000   | 052   | 0038.23 ms     |
| 64th  | Dr R Dizzle          | LisaSimpson             | Ruby       | 016   | 016   | 002   | 051   | 0038.29 ms     |
| 65th  | Martin Büttner       | Vulcan                  | Ruby       | 015   | 015   | 001   | 053   | 0038.26 ms     |
| 65th  | Dr R Dizzle          | Khaleesi                | Ruby       | 015   | 015   | 005   | 049   | 0038.29 ms     |
| 67th  | Dr R Dizzle          | EdwardScissorHands      | Ruby       | 014   | 014   | 002   | 053   | 0038.21 ms     |
| 67th  | undergroundmonorail  | TheGambler              | Python2    | 014   | 014   | 002   | 053   | 0025.47 ms     |
| 69th  | cipher               | LemmingBot              | Python2    | 011   | 011   | 002   | 056   | 0025.29 ms     |
| 70th  | Docopoper            | ConcessionBot           | Python2    | 007   | 007   | 000   | 062   | 0141.31 ms     |
+-------+----------------------+-------------------------+------------+-------+-------+-------+-------+----------------+
Total Players: 70
Total Matches Completed: 2415
Total Tourney Time: 06:00:51.6877573

Tourney Notes

Excluded Bots

  • BashRocksBot - still no joy with .net execing cygwin bash scripts
  • CounterPreferenceBot - awaiting bug fix
  • RandomlyWeighted - awaiting bug fix
  • CasinoShakespeare - excluded because it requires an active internet connection

Original Posted Question

You've swung around to your friends house for the most epic showdown Battle ever of Rock, Paper, Scissors, Lizard, Spock. In true BigBang nerd-tastic style, none of the players are playing themselves but have created console bots to play on their behalf. You whip out your USB key and hand it over to the Sheldor the Conqueror for inclusion in the showdown. Penny swoons. Or perhaps Howard swoons. We don't judge here at Leonard's apartment.

Rules

Standard Rock, Paper, Scissors, Lizard, Spock rules apply.

  • Scissors cut Paper
  • Paper covers Rock
  • Rock crushes Lizard
  • Lizard poisons Spock
  • Spock smashes Scissors
  • Scissors decapitate Lizard
  • Lizard eats Paper
  • Paper disproves Spock
  • Spock vaporizes Rock
  • Rock crushes Scissors

RPSLV Rules

Each player's bot will play one Match against each other bot in the tournament.

Each Match will consist of 100 iterations of an RPSLV game.

After each match, the winner is the player who has won the most number of games/hands out of 100.

If you win a match, you will be assigned 1 point in the league table. In the result of a draw-match, neither player will gain a point.

Bot Requirements

Your bot must be runnable from the command line.

Sheldor's *nix box has died, so we're running it off his windows 8 Gaming Laptop so make sure your provided solution can run on windows. Sheldor has graciously offered to install any required runtimes (within reason) to be able to run your solution. (.NET, Java, Php, Python, Ruby, Powershell ...)

Inputs

In the first game of each match no arguments are supplied to your bot. In each subsequent game of each match: - Arg1 will contain the history of your bots hands/decisions in this match. - Arg2 will contain the history of your opponents hands/decisions in this match.

History will be represented by a sequence of single capital letters representing the possible hands you can play.

 | R | Rock     |
 | P | Paper    |
 | S | Scissors |
 | L | Lizard   |
 | V | Spock    |

E.g.

  • Game 1: MyBot.exe
  • Game 2: MyBot.exe S V
  • Game 3: MyBot.exe SS VL
  • Game 4: MyBot.exe SSR VLS

Output

Your bot must write a single character response representing his "hand" for each game. The result should be written to STDOUT and the bot should then exit. Valid single capital letters are below.

 | R | Rock     |
 | P | Paper    |
 | S | Scissors |
 | L | Lizard   |
 | V | Spock    |

In the case where your bot does not return a valid hand (i.e. 1 of the above 5 single capital letters, then you automatically forfeit that hand and the match continues.

In the case where both bots do not return a valid hand, then the game is considered a draw and the match continues.

Match Format

Each submitted bot will play one match against each other bot in the tournament.

Each match will last exactly 100 games.

Matches will be played anonymously, you will not have an advanced knowledge of the specific bot you are playing against, however you may use any and all information you can garner from his decision making during the history of the current match to alter your strategy against your opponent. You may also track history of your previous games to build up patterns/heuristics etc... (See rules below)

During a single game, the orchestration engine will run your bot and your opponents bot 100 milliseconds apart and then compare the results in order to avoid any PRNG collisions in the same language/runtime. (this actually happened me during testing).

Judging & Constraints

Dr. Sheldon Cooper in the guise of Sheldor the Conqueror has kindly offered to oversee the running of the tournament. Sheldor the Conqueror is a fair and just overseer (mostly). All decisions by Sheldor are final.

Gaming will be conducted in a fair and proper manner:

  • Your bot script/program will be stored in the orchestration engine under a subfolder Players\[YourBotName]\
  • You may use the subfolder Players\[YourBotName]\data to log any data or game history from the current tournament as it proceeds. Data directories will be purged at the start of each tournament run.
  • You may not access the Player directory of another player in the tournament
  • Your bot cannot have specific code which targets another specific bots behavior
  • Each player may submit more than one bot to play so long as they do not interact or assist one another.

Edit - Additional Constraints

  • Regarding forfeits, they won't be supported. Your bot must play one of the 5 valid hands. I'll test each bot outside of the tournament with some random data to make sure that they behave. Any bots that throw errors (i.e. forfeits errors) will be excluded from the tourney til they're bug fixed.
  • Bots may be derivative so long as they are succinctly different in their behaviour. Bots (including in other languages) that perform exactly the same behaviour as an existing bot will be disqualified
  • There are already spam bots for the following so please don't resubmit
    • Rock - BartSimpson
    • Paper - LisaSimpson
    • Scissor - EdwardScissorhands
    • Spock - Vulcan
    • Lizard - Khaleesi
    • Pseudo Random - SimpleRandomBot & FairBot
    • Psuedo Random RPS - ConservativeBot
    • Psuedo Random LV - Barney Stinson
  • Bots may not call out to 3rd party services or web resources (or anything else which significantly slows down the speed/decision making time of the matches). CasinoShakespeare is the only exception as that bot was submitted prior to this constraint being added.

Sheldor will update this question as often as he can with Tournament results, as more bots are submitted.

Orchestration / Control Program

The orchestration program, along with source code for each bot is available on github.

https://github.com/eoincampbell/big-bang-game

Submission Details

Your submission should include

  • Your Bot's name
  • Your Code
  • A command to
    • execute your bot from the shell e.g.
    • ruby myBot.rb
    • python3 myBot.py
    • OR
    • first compile your both and then execute it. e.g.
    • csc.exe MyBot.cs
    • MyBot.exe

Sample Submission

BotName: SimpleRandomBot
Compile: "C:\Program Files (x86)\MSBuild\12.0\Bin\csc.exe" SimpleRandomBot.cs
Run:     SimpleRandomBot [Arg1] [Arg2]

Code:

using System;
public class SimpleRandomBot
{
    public static void Main(string[] args)
    {
        var s = new[] { "R", "P", "S", "L", "V" };
        if (args.Length == 0)
        {
            Console.WriteLine("V"); //always start with spock
            return;
        }
        char[] myPreviousPlays = args[0].ToCharArray();
        char[] oppPreviousPlays = args[1].ToCharArray();
        Random r = new Random();
        int next = r.Next(0, 5);
        Console.WriteLine(s[next]);
    }
}

Clarification

Any questions, ask in the comments below.

share|improve this question
7  
What does the history look like when a player has forfeited a hand? –  histocrat Jul 25 at 17:41
1  
I was going to go all-out with an analytic approach, but most of the bots here are stupid enough to defeat smart AI. –  fluffy Jul 25 at 21:23
1  
Just because I never am in the lead for any KotH challenge I've competed in, I've taken a screenshot as a memento. –  Kyle Kanos Jul 26 at 2:55
3  
I'll run another tourney tonight and post the full match results on pastebin... next batch will have about 450 games but should be a bit quicker to run as I've implemented some parallelization stuff in the control prog –  Eoin Campbell Jul 28 at 7:55
3  
If I'm not mistaken, there seems to be a serious bug in the orchestration script: The histories of player 1 and 2 are always passed to the bots as first and second argument respectively, while according to the rules the bots should always get their own history first. Now player 2 is effectively trying to beat itself. (I got a bit suspicious because my bot won every single match where it was player 1 while losing half of the other matches.) –  Emil Jul 29 at 7:07

75 Answers 75

Pony (Python 2)

This is based on a rock-paper-scissors bot I wrote some time ago for a programming challenge at the end of a Udacity online class. I changed it to include Spock and lizard and made some improvements.

The program has 11 different simple strategies, each with 5 variants. It chooses from among these based on how well they would have performed over the last rounds.

I removed a fallback strategy that just played random against stronger opponents. I guess it's more fun like this.

import sys

# just play Spock for the first two rounds
if len(sys.argv)<2 or len(sys.argv[1])<2: print 'V'; sys.exit()

# initialize and translate moves to numbers for better handling:
my_moves, opp_moves = sys.argv[1], sys.argv[2]
moves = ('R', 'P', 'S', 'V', 'L')   
history = zip([moves.index(i) for i in my_moves],
              [moves.index(i) for i in opp_moves])

# predict possible next moves based on history
def prediction(hist):
    N = len(hist)    

    # find longest match of the preceding moves in the earlier history
    cand_m = cand_o = cand_b = range(N-1)
    for l in xrange(1,min(N, 20)):
        ref = hist[N-l]
        cand_m = ([c for c in cand_m if c>=l and hist[c-l+1][0]==ref[0]]
                  or cand_m[-1:])
        cand_o = ([c for c in cand_o if c>=l and hist[c-l+1][1]==ref[1]]
                  or cand_o[-1:])
        cand_b = ([c for c in cand_b if c>=l and hist[c-l+1]==ref]
                  or cand_b[-1:])

    # analyze which moves were used how often
    freq_m, freq_o = [0]*5, [0]*5
    for m in hist:
        freq_m[m[0]] += 1
        freq_o[m[1]] += 1

    # return predictions
    return ([hist[-i][p] for i in 1,2 for p in 0,1]+   # repeat last moves
            [hist[cand_m[-1]+1][0],     # history matching of my own moves
             hist[cand_o[-1]+1][1],     # history matching of opponent's moves
             hist[cand_b[-1]+1][0],     # history matching of both
             hist[cand_b[-1]+1][1],
             freq_m.index(max(freq_m)), # my most frequent move
             freq_o.index(max(freq_o)), # opponent's most frequent move
             0])                        # good old rock (and friends)


# what would have been predicted in the last rounds?
pred_hist = [prediction(history[:i]) for i in xrange(2,len(history)+1)]

# how would the different predictions have scored?
n_pred = len(pred_hist[0])
scores = [[0]*5 for i in xrange(n_pred)]
for pred, real in zip(pred_hist[:-1], history[2:]):
    for i in xrange(n_pred):
        scores[i][(real[1]-pred[i]+1)%5] += 1
        scores[i][(real[1]-pred[i]+3)%5] += 1
        scores[i][(real[1]-pred[i]+2)%5] -= 1
        scores[i][(real[1]-pred[i]+4)%5] -= 1

# return best counter move
best_scores = [list(max(enumerate(s), key=lambda x: x[1])) for s in scores]
best_scores[-1][1] *= 1.001   # bias towards the simplest strategy    
if best_scores[-1][1]<0.4*len(history): best_scores[-1][1] *= 1.4
strat, (shift, score) = max(enumerate(best_scores), key=lambda x: x[1][1])
print moves[(pred_hist[-1][strat]+shift)%5]

Run as:

python Pony.py

Edit: I made a small change by putting in a bias towards the simplest strategy (i.e. always play the same move) in unsure cases. This helps a bit to not try to find overly complicated patterns where there are none, e.g. in bots like ConservativeBot.

Note: I tried to explain the basic history matching strategy that this bot uses in the post for my other bot Dienstag.

share|improve this answer
    
A 96 percent win ratio is outstanding. –  AndoDaan Jul 31 at 0:56
    
Very nice. You may like Iocaine Powder, if you haven't seen it already. –  WChargin Jul 31 at 5:14
    
@WChargin, of course. :) When I wrote my original code, I had read about Iocaine Powder some years earlier and vaguely remembered the general idea. So, Pony is indeed inspired by it, if not very directly. As it turns out, they are very similar. I think mine has a wider repertoire of strategies while Iocaine Powder has a clever level of meta-meta reasoning that I did not include. –  Emil Jul 31 at 6:00

Markov, Ruby

Looks at the opponent's last two moves and determines the possible (and most likely) follow ups. If the combination hasn't been picked before, he just uses all of the opponent's moves (so far) instead. Then he collects all the possible responses for these and picks a random one.

responses = {
  'R' => ['P', 'V'],
  'P' => ['S', 'L'],
  'S' => ['R', 'V'],
  'L' => ['S', 'R'],
  'V' => ['P', 'L']
}

if ARGV.length == 0 || (history = ARGV[1]).length < 3
    choices = ['R','P','S','L','V']
else
    markov = Hash.new []
    history.chars.each_cons(3) { |chars| markov[chars[0..1].join] += [chars[2]] }

    choices = []
    likely_moves = markov.key?(history[-2,2]) ? markov[history[-2,2]] : history.chars
    likely_moves.each { |move| choices += responses[move] }
end

puts choices.sample

Run like

markov.rb
share|improve this answer
    
And then I use this program to determine the most possible move that I will do next then find out what you will do and finally find a way to beat what you will do and infinite-loop the whole thing again, again and again. –  Jamie Jul 29 at 0:35
    
@Jamie You mean like this guy? codegolf.stackexchange.com/a/35295/8478 –  Martin Büttner Jul 29 at 6:51
    
you guess it. (the comment was not long enough to be posted) –  Jamie Jul 29 at 23:22

ConservativeBot, Ruby

New things are bad things.

puts ['R','P','S'].sample

Run like

ruby conservative.rb
share|improve this answer
    
OG version is the best version. –  maxywb Jul 25 at 16:25
    
Added to Controller –  Eoin Campbell Jul 25 at 17:51
1  
@EoinCampbell I don't think it's necessary to inform everyone in a comment. It just clutters the page. I suggest that you instead just mention which bots didn't make it into a leaderboard, although they were already posted, like Doorknob does here. –  Martin Büttner Jul 25 at 18:09
    
sure. good suggestion. –  Eoin Campbell Jul 25 at 18:17

Barney Stinson - Lua

I only have one rule: New is always better. Screw old Jo Ken Po or whatever you call it.

math.randomseed(os.time())
print(math.random() > 0.5 and "V" or "L")

Run it like:

lua legenwaitforitdary.lua
share|improve this answer

Star Wars Fan - Ruby

Screw you, Spock

puts ['R','P','L','S'].sample

Run it like:

ruby starwarsfan.rb
share|improve this answer
    
Added to Controller –  Eoin Campbell Jul 25 at 17:50
    
you can rollback by answer edit - I'll just comment here when I've added them. –  Eoin Campbell Jul 25 at 17:50
    
Why R and S? :P –  cjfaure Jul 26 at 13:48
    
@mardavi It is a Star Wars fan because it does not use Spock. –  William Barbosa Jul 28 at 10:47
    
ah, you are right (of course). I read it too fast, my mistake (but without consequences luckily) –  mardavi Jul 28 at 11:05

Boring Bot (Java)

He assumes everyone always plays the same thing and plans accordingly. He usually picks rocks in a ties though ’cause so does everyone else right?

public class BoringBot
{
    public static void main(String[] args)
    {
        int Rock=0;
        int Paper=0;
        int Scissors=0;
        int Lizard=0;
        int Spock=0;

        if (args.length == 0)
        {
            System.out.print("P");
            return;
        }

        char[] oppPreviousPlays = args[1].toCharArray();

        for (int j=0; j<oppPreviousPlays.length; j++) {
            switch(oppPreviousPlays[j]){
                case 'R': Rock++; break;
                case 'P': Paper++; break;
                case 'S': Scissors++; break;
                case 'L': Lizard++; break;
                case 'V': Spock++;
            }
        }

        int Best = Math.max(Math.max(Lizard+Scissors-Spock-Paper,
                                     Rock+Spock-Lizard-Scissors),
                            Math.max(Math.max(Paper+Lizard-Spock-Rock,
                                              Paper+Spock-Rock-Scissors),
                                     Rock+Scissors-Paper-Lizard));

        if (Best== Lizard+Scissors-Spock-Paper){
            System.out.print("R"); return;
        } else if (Best== Rock+Spock-Lizard-Scissors){
            System.out.print("P"); return;
        } else if (Best== Paper+Lizard-Spock-Rock){
            System.out.print("S"); return;
        } else if(Best== Paper+Spock-Rock-Scissors){
            System.out.print("L"); return;
        } else {
            System.out.print("V"); return;
        }
    }
}
share|improve this answer
    
Note tht if this is a strategy that someone else is already using let me know and I'll delete. It just feels like the obvious one I didn't already see. –  kaine Jul 25 at 18:15
    
is this C#. you're .length properties are wrong. and theres no method max –  Eoin Campbell Jul 25 at 18:46
    
@EoinCampbell It is java, i've been playing with both and apparently forgot which commands belong to which. –  kaine Jul 25 at 19:09
    
ah cool. leave it with me and I'll get it included. –  Eoin Campbell Jul 25 at 19:12
    
still broken. running jre8 - java BoringBot.java - Error: Could not find or load main class D:\My Software Dev\big-bang-game\BigBang.Orchestrator\bin\Debug\Players\BoringBot\BoringBot.jav‌​a - –  Eoin Campbell Jul 26 at 0:24

HuddleWolfTheConqueror - C#

HuddleWolf is back and better than ever. He will beat Sheldor the Conqueror at his own silly game. HuddleWolf is smart enough to identify and counter spammerbots. For more intelligent opponents, HuddleWolf uses his knowledge of basic 5th grade statistics and utilizes a weighted dice roll based on the opposition's history of plays.

using System;
using System.Collections.Generic;
using System.Linq;

public class HuddleWolfTheConqueror
{

    public static readonly char[] s = new[] { 'R', 'P', 'S', 'L', 'V' };

    public static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine(pickRandom());
            return;
        }

        char[] myPlays = args[0].ToCharArray();
        char[] oppPlays = args[1].ToCharArray();

        char tryPredict = canPredictCounter(oppPlays);
        if (tryPredict != '^')
        {
            Console.WriteLine(tryPredict);
        }
        else
        {
            Console.WriteLine(pickRandom());
        }
        return;
    }


    public static char canPredictCounter(char[] history)
    {
        // don't predict if insufficient data
        if (history.Length < 5)
        {
            return '^';
        }

        // calculate probability of win for each choice
        Dictionary<char, double> dic = getBestProabability(history);

        // get item with highest probability of win
        List<char> maxVals = new List<char>();
        char maxVal = '^';
        double mostFreq = 0;
        foreach (var kvp in dic)
        {
            if (kvp.Value > mostFreq)
            {
                mostFreq = kvp.Value;
            }
        }
        foreach (var kvp in dic)
        {
            if (kvp.Value == mostFreq)
            {
                maxVals.Add(kvp.Key);
            }
        }

        // return error
        if (maxVals.Count == 0)
        {
            return maxVal;
        }

        // if distribution is not uniform, play best play
        if (maxVals.Count <= 3)
        {
            Random r = new Random(Environment.TickCount);
            return maxVals[r.Next(0, maxVals.Count)];
        }

        // if probability is close to uniform, use weighted dice roll
        if (maxVals.Count == 4)
        {
            return weightedRandom(dic);
        }

        // if probability is uniform, use random dice roll
        if (maxVals.Count >= 5)
        {
            return pickRandom();
        }

        // return error
        return '^';
    }

    public static Dictionary<char, double> getBestProabability(char[] history)
    {
        Dictionary<char, double> dic = new Dictionary<char, double>();
        foreach (char c in s)
        {
            dic.Add(c, 0);
        }
        foreach (char c in history)
        {
            if (dic.ContainsKey(c))
            {
                switch(c)
                {
                    case 'R' : 
                        dic['P'] += (1.0/(double)history.Length);
                        dic['V'] += (1.0/(double)history.Length);
                        break;
                    case 'P' : 
                        dic['S'] += (1.0/(double)history.Length);
                        dic['L'] += (1.0/(double)history.Length);
                        break;
                    case 'S' : 
                        dic['V'] += (1.0/(double)history.Length);
                        dic['R'] += (1.0/(double)history.Length);
                        break;
                    case 'L' : 
                        dic['R'] += (1.0/(double)history.Length);
                        dic['S'] += (1.0/(double)history.Length);
                        break;
                    case 'V' : 
                        dic['L'] += (1.0/(double)history.Length);
                        dic['P'] += (1.0/(double)history.Length);
                        break;
                    default : 
                        break;

                }
            }
        }
        return dic;
    }

    public static char weightedRandom(Dictionary<char, double> dic)
    {
        Random r = new Random(Environment.TickCount);
        int next = r.Next(0, 100);
        int curVal = 0;
        foreach (var kvp in dic)
        {
            curVal += (int)(kvp.Value*100);
            if (curVal > next)
            {
                return kvp.Key;
            }
        }
        return '^';
    }

    public static char pickRandom()
    {
        Random r = new Random(Environment.TickCount);
        int next = r.Next(0, 5);
        return s[next];
    }
}
share|improve this answer

ToddlerProof

This fairly stupid bot assumes it is playing a toddler who will "chase" its moves, always trying to beat whatever was last thrown. If the bot is beaten several times in a row, it jumps to a new point in the pattern. It is based on my strategy for always beating my much younger brother. :)

EDIT:: Changed the length of a loss streak required to jump into random throws. Also fixed a major bug with the random jump.

Save as ToddlerProof.java, compile, then run with java ToddlerProof [me] [them]

import java.util.HashMap;
public class ToddlerProof
{
    char[] moves = new char[]{'R', 'P', 'S', 'L', 'V'};
    public static void main(String[] args)
    {
        if(args.length<1) //first Round
        {
            System.out.print('V');//Spock is best
            return;
        }
        else
        {
            String them = args[1];
            String me = args[0];
            int streak = 0;

            HashMap<Character, Character> nextMove = new HashMap<Character, Character>();
            //Next move beats things that beat my last move
            nextMove.put('L', 'V');
            nextMove.put('V', 'S');
            nextMove.put('S', 'P');
            nextMove.put('P', 'R');
            nextMove.put('R', 'L');
            //Check if last round was a tie or the opponent beat me
            int lastResult = winner(me.charAt(me.length()-1), them.charAt(them.length()-1));
            if(lastResult == 0)
            {
                //tie, so they will chase my last throw
                System.out.print(nextMove.get(me.charAt(me.length()-1)));

                return;
            }
            else if(lastResult == 1)
            {
                //I won, so they will chase my last throw
                System.out.print(nextMove.get(me.charAt(me.length()-1)));


                return;
            }

            else{
                //I lost
                //find streak
                for(int i = 0; i<me.length(); i++)
                {
                    int a = winner(me.charAt(i), them.charAt(i));
                    if(a >= 0) streak = 0;
                    else streak++;
                }
                //check lossStreak
                //If the streak is 2, then a rotation will make it even.
                //if it is >2, something bad has happened and I need to adjust.
                if(streak>2)
                {
                    //if they are on to me, do something random-ish
                    int r = (((them.length()+me.length()-1)*13)/7)%4;
                    System.out.print(move[r]);
                    return;
                }
                //otherwise, go on with the plan
                System.out.print(nextMove.get(me.charAt(me.length()-1)));
                return;
            }
        }
    }
    public static int winner(char me, char them)
    {
        //check for tie
        if(me == them) return 0;
        //check if they won
        if(me=='V' && (them == 'L' || them == 'P')) return -1;
        if(me=='S' && (them == 'V' || them == 'R')) return -1;
        if(me=='P' && (them == 'S' || them == 'L')) return -1;
        if(me=='R' && (them == 'P' || them == 'V')) return -1;
        if(me=='L' && (them == 'R' || them == 'S')) return -1;
        //otherwise, I won
        return 1;
    }
}
share|improve this answer
1  
Should we be using print or println?... I wasn't certain. –  kaine Jul 25 at 20:31
    
Hmmm. I would imagine both would work, but I could see println messing up if the control program grabbed the newline instead of the character. Thanks for pointing that out, I'll edit my code just in case –  Stranjyr Jul 25 at 20:34
    
@Stranjyr there were some bugs in your last run. It didn't bomb the control program but if you search the history for "ToddlerProof plays n" it looks like your bot was returning null for certain hands and then autolosing the hand. Example game is "Echo & ToddlerProof" where Echo played "LVSPRLV" before your bot started to crap out. –  Eoin Campbell Jul 30 at 13:09
    
@Eion Campbell Thanks for mentioning it. I saw that earlier when you posted the logs from the failed tourney, and I think I have it fixed. It was running into an error where if it lost more than 5 straight, instead of jumping to a random play it just threw an invalid value. And then, because that made it lose, it threw another invalid value. A vicious cycle. –  Stranjyr Jul 30 at 13:32
    
Cool. Have it updated in the control prog now. –  Eoin Campbell Jul 31 at 17:09

Bart Simpson

"Good old rock! Nothing beats rock!"

puts 'R'

Run as

ruby DoTheBartman.rb

Lisa Simpson

"Poor, predictable Bart. Always chooses rock."

puts 'P'

Run as

ruby LisaSimpson.rb

Better Lisa Simpson

I felt bad about making Lisa quite so stupid, so I allowed her to randomly choose between either of the hands that will beat rock. Still stupid, but she is a Simpson after all. Maybe a crayon got stuck in her brain?

puts ['P','V'].sample

Run as

ruby BetterLisaSimpson.rb
share|improve this answer
2  
Minor name clash. +1 anyway. –  Martin Büttner Jul 28 at 14:06
    
@MartinBüttner Damn, didn't notice that. The programs still seem to do different things though - and at least Lisa here can feel more superior by beating two different versions of her brother. –  Dr R Dizzle Jul 28 at 14:17
1  
Sheldor agrees... there shall be a BartBot and a BartSimpson :) –  Eoin Campbell Jul 28 at 15:36
3  
We only have BortBot. –  JoshDM Jul 28 at 18:42
1  
These will get slaughtered by markov :) –  Cruncher Jul 29 at 19:03

Echo

Written in C#. Compile with csc Echo.cs. Run like Echo.exe ARG1 ARG2.

The first run, Echo takes a random option. Every run after the first, Echo simply repeats the opponent's latest action.

using System;

namespace Echo
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Random r = new Random();
                string[] options = new string[] { "R", "P", "S", "L", "V" };
                Console.WriteLine(options[r.Next(0, options.Length)]);
            }
            else if (args.Length == 2)
            {
                string opponentHistory = args[1];
                Console.WriteLine(opponentHistory[opponentHistory.Length - 1]);
            }
        }
    }
}
share|improve this answer

Vulcan, Ruby

My fingers are glued together.

puts 'V'

Run like

ruby vulcan.rb

(I think this is the only in-character strategy for your background setting.)

share|improve this answer
    
Need to look back at the episodes to see if anyone was born with a forked tongue. LizardMan FTW !!! –  Eoin Campbell Jul 25 at 17:05
3  
But isn't this how everyone on big bang plays anyways? –  kaine Jul 25 at 17:24
1  
@anotherguest That's what I meant by "this is the only in-character strategy". –  Martin Büttner Jul 25 at 17:24

IocainePowder, Ruby

enter image description here

Based off (shamelessly stolen from) the RPS strategy here. The bot looks chooses a guess identical to the Markov bot, but then assumes the opponent has guessed what it's going to choose, and chooses a move to beat that one accordingly.

Note that I've just adapted the basic idea of the linked strategy, not followed it in detail.

responses = {
  'R' => ['P', 'V'],
  'P' => ['S', 'L'],
  'S' => ['R', 'V'],
  'L' => ['S', 'R'],
  'V' => ['P', 'L']
}

if ARGV.length == 0 || (history = ARGV[1]).length < 3
    choices = ['R','P','S','L','V']
else
    markov = Hash.new []
    history.chars.each_cons(3) { |chars| markov[chars[0..1].join] += [chars[2]] }

    choices = []
    likely_moves = markov.key?(history[-2,2]) ? markov[history[-2,2]] : history.chars
    likely_moves.each { |move| choices += responses[move] }
end

myChoice = choices.sample 
theirChoice = responses[myChoice].sample
actualChoice = responses[theirChoice].sample
puts actualChoice

Run like

iocaine.rb
share|improve this answer
5  
You keep using that word. I do not think it means what you think it means. –  JoshDM Jul 28 at 20:25
2  
The real power of Iocaine Powder was that is switches between using the markov and beating-markov. It starts out as smart markov, but once it senses (starts losing) it jumps into beating-markov mode. Should be easy to add. –  Roy van Rijn Jul 30 at 14:39
    
Ahh, clever! Not gonna lie, I had only heard Iocaine described to me, not actually looked at it in detail. Feel free to modify my code if you'd like or submit your own and get the credit! –  jmite Jul 30 at 17:11

Tyrannosaurus, Godzilla, Barney... Lizards Rule. Occasionally they get in trouble and need to call Spock or throw Rocks

using System;
public class LizardsRule
{
    public static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("L");
            return;
        }
        char[] oppPreviousPlays = args[1].ToCharArray();
        var oppLen = oppPreviousPlays.Length;
        if (oppPreviousPlays.Length > 2
            && oppPreviousPlays[oppLen - 1] == 'R'
            && oppPreviousPlays[oppLen - 2] == 'R'
            && oppPreviousPlays[oppLen - 3] == 'R')
        {
            //It's an avalance, someone call Spock
            Console.WriteLine("V");
            return;
        }

        if (oppPreviousPlays.Length > 2
                && oppPreviousPlays[oppLen - 1] == 'S'
                && oppPreviousPlays[oppLen - 2] == 'S'
                && oppPreviousPlays[oppLen - 3] == 'S')
        {
            //Scissors, Drop your tail and pick up a rock
            Console.WriteLine("R");
            return;
        }

        //Unleash the Fury Godzilla
        Console.WriteLine("L");     
    }
}
share|improve this answer

DynamicBot

Dynamic bot is almost always changing. It really hates repeating itself

import sys, random
choices = ['L','V','S','P','R'] * 20
if len(sys.argv) > 1:
    my_history = sys.argv[1]
    [choices.remove(my_history[-1]) for i in range(15)]
print(choices[random.randrange(len(choices))])

Language: Python 3.4.1

Command: python dynamicbot.py <history> or python3 dynamicbot.py <history> depending on your system

share|improve this answer
    
Yeah, thought about that. –  Sieg Jul 28 at 8:15

SpockOrRock - PHP

SpockOrRock

When played in the real world, most people instinctively pick scissors. This bot picks either Spock or Rock to beat the average player. It's not bothered about previous rounds.

run with php spockorrock.php

<?php

//Pick either Spock or Rock
if (rand(0,1) == 0)     echo("R\n");
else                    echo("V\n");


?>
share|improve this answer

BayesianBot, Perl (now v2!)

Above everything else, this is a unique program. In it, you shall see the brilliant fusion of statistics and horrible programming form. Also, this bot probably breaks many rules of Bayesian statistics, but the name sounds cooler.

The core essence of this bot is its creation of 250 different predictive models. Each model takes the form of "Given that I played rock last turn and my opponent played scissors two turns ago, this is the probability distribution for my opponent's next move." Each probability distribution takes the form of a multi-dimensional Dirichlet distribution.

Each turn, the predictions of all applicable models (typically 10) are multiplied together to form an overall prediction, which is then used to determine which moves have the highest expected payoff.

Edit 1: In this version, I changed the prior distribution and made the bot more randomized when it is losing.

There are a few things which may be subject to improvement, such as the number of models (250 is only a 3 digit number), the choice of prior distribution (currently Dir(3,3,3,3,3)), and the method of fusing predictions. Also, I never bothered to normalize any of the probability distributions, which is okay for now because I'm multiplying them.

I don't have super high expectations, but I hope this bot will be able to do well.

my ($phist, $ohist) = @ARGV;

my %text2num = ('R',0,'V',1,'P',2,'L',3,'S',4);  #the RVPLS ordering is superior
my @num2text = ('R','V','P','L','S');

@phist = map($text2num{$_},split(//,$phist));
@ohist = map($text2num{$_},split(//,$ohist));

$lowerlimit = 0;
for($lowerlimit..~~@phist-3){$curloc=$_;
 $result = $ohist[$curloc+2];
 @moveset = ($ohist[$curloc],$ohist[$curloc+1],$phist[$curloc],$phist[$curloc+1]);
 for(0..3){$a=$_;
  for(0..$a){$b=$_;
   $predict[$a][$b][$moveset[$a]][$moveset[$b]][$result]++;
  }
 }
}

@recentmoves = ($ohist[-2],$ohist[-1],$phist[-2],$phist[-1]);

@curpred = (1,1,1,1,1);

for(0..3){$a=$_;
 for(0..$a){$b=$_;
  for(0..4){$move=$_;
   $curpred[$move] *= $predict[$a][$b][$recentmoves[$a]][$recentmoves[$b]][$move]/3+1;
  }
 }
}

@bestmove = (0,0,0,0,0);
for(0..4){
 $bestmove[$_] = $curpred[$_]/2+$curpred[$_-1]+$curpred[$_-2];
}

$max = 0;
for(0..4){
 if($bestmove[$_]>$max){
  $max = $bestmove[$_];
 }
}
@options=();
$offset=0;
if(($ohist[-1] - $phist[-1])%5 < 2 && ($ohist[-2] - $phist[-2])%5 < 2 && ($ohist[-3] - $phist[-3])%5 < 2){  #frequentist alert!
 $offset=int(rand(3));
}
for(0..4){
 if($bestmove[$_] == $max){
  push(@options,$num2text[($_+$offset)%5]);
 }
}
$outputb = $options[int(rand(~~@options))];

print "$outputb";

I've been running this program like so:

perl BayesianBot.plx
share|improve this answer
    
Updated.......... –  Eoin Campbell Jul 29 at 17:04

SlowLizard, Ruby

After starting with Lizard, it always picks a random move which beats the opponent's previous move.

responses = {
  'R' => ['P', 'V'],
  'P' => ['S', 'L'],
  'S' => ['R', 'V'],
  'L' => ['S', 'R'],
  'V' => ['P', 'L']
}

if ARGV.length == 0
  puts 'L'
else
  puts responses[ARGV[1][-1]].sample
end

Run like

ruby slowlizard.rb
share|improve this answer

LexicographicBot

This bot likes to order his letters, so he will choose a response that is 1 higher than his opponent gave in the previous round--unless the opponent chose Vulcan, then he randomly picks a response.

import sys
import random

choices = ["L", "P", "R", "S", "V"]

total = len(sys.argv)
if total==1:
    print("L")
    sys.exit()

opponent = sys.argv[2]
opponent_last = opponent[-1]

if opponent_last == choices[-1]:
    print(random.choice(choices))
else:
    next = choices.index(opponent_last)+1
    print(choices[next])

This expects the opponent hand to be dealt second:

                           me
                            v
python LexicographicBot.py SR RV
                              ^
                            opponent
share|improve this answer
    
@MartinBüttner: Command added! I have been pretty busy at work trying to get something published, hence the disappearance. –  Kyle Kanos Jul 25 at 16:31
    
breaks on first run with no args. Traceback (most recent call last): File "LexicographicBot\LexicographicBot.py", line 10, in <module> opponent = sys.argv[2] IndexError: list index out of range –  Eoin Campbell Jul 25 at 19:30
    
@EoinCampbell: I forgot the exit clause on first run, it's been added & should work fine now. –  Kyle Kanos Jul 25 at 19:55
    
yep. fixed now. thanks –  Eoin Campbell Jul 25 at 20:05

SmartBot - Java

My first ever entry for anything on this site!

Though not a very creative name...

SmartBot finds sequences of moves where the opponent and/or itself's moves are similar to the moves last made and plans accordingly.

name = SmartBot

I think to run it, correct me if I am wrong.

java -jar SmartBot.jar

import java.util.ArrayList;
public class SmartBot {
    public static void main(String[] args) {
        if(args.length ==0){
            System.out.print("L");
            return;
        }
        if(args[0].length()<3){
            String[] randLetter = new String[]{"R","P","S","L","V"};
            System.out.print(randLetter[(int) Math.floor(Math.random()*5)]);
            return;
        }
        String myHistory = args[0];
        String otherHistory = args[1];

        double rScore,pScore,sScore,lScore,vScore;//score - highest = highest probability of next opponent move
        rScore = pScore = sScore = lScore = vScore = 0;
        lScore = .001;
        ArrayList<ArrayList<Integer>> moveHits = new ArrayList<ArrayList<Integer>>();
        for(int g = 0;g<2;g++){
            for(int i=1;i<(myHistory.length() / 2) + 1;i++){
                if(g==0){
                    moveHits.add(findAll(myHistory.substring(myHistory.length() - i),myHistory));
                }
                else{
                    moveHits.add(findAll(otherHistory.substring(otherHistory.length() - i),otherHistory));
                }
            }
            for(int i = 0; i < moveHits.size();i++){
                int matchingMoves = i+1;
                ArrayList<Integer> moveIndexes = moveHits.get(i);
                for(Integer index:moveIndexes){
                    if(index+matchingMoves +1<= otherHistory.length()){
                        char nextMove = otherHistory.charAt(index + matchingMoves-1);
                        if(nextMove=='R'){rScore = rScore + matchingMoves;}
                        if(nextMove=='P'){pScore = pScore + matchingMoves;}
                        if(nextMove=='S'){sScore = sScore + matchingMoves;}
                        if(nextMove=='L'){lScore = lScore + matchingMoves;}
                        if(nextMove=='V'){vScore = vScore + matchingMoves;}
                    }
                }
            }
        }
        if(rScore >= pScore && rScore >= sScore && rScore >= lScore && rScore >= vScore){
            System.out.print("V");
            return;
        }
        if(pScore >= rScore && pScore >= sScore && pScore >= lScore && pScore >= vScore){
            System.out.print("L");
            return;
        }
        if(sScore >= pScore && sScore >= rScore && sScore >= lScore && sScore >= vScore){
            System.out.print("R");
            return;
        }
        if(vScore >= pScore && vScore >= sScore && vScore >= lScore && vScore >= rScore){
            System.out.print("L");
            return;
        }
        if(lScore >= pScore && lScore >= sScore && lScore >= rScore && lScore >= vScore){
            System.out.print("S");
        }
        return;
    }
    public static ArrayList<Integer> findAll(String substring,String realString){
        ArrayList<Integer> ocurrences = new ArrayList<Integer>();
        Integer index = realString.indexOf(substring);
        if(index==-1){return ocurrences;}
        ocurrences.add(index+1);
        while(index!=-1){
            index = realString.indexOf(substring,index + 1);
            if(index!=-1){
                ocurrences.add(index+1);
            }
        }
        return ocurrences;
    }
}

It assigns a score for each possible next move by the number of times similar patterns have happened.

It slightly favors lizard.

share|improve this answer
    
I believe that's how you run it if you jar it first. If you simply compile it first, then java ABot should work (remember to name the file the same as the public class) –  Quincunx Jul 26 at 4:49
    
Thanks! As a relatively new programmer I was not aware of this. –  Stretch Maniac Jul 26 at 13:39

Analogizer - Ruby

Run with ruby analogizer.rb. I've made a logic fix to the code, but no idea why there were errors with this.

@rules = {

  'L' => %w[V P],
  'P' => %w[V R],
  'R' => %w[L S],
  'S' => %w[P L],
  'V' => %w[R S]
}

@moves = @rules.keys

def defeats?(move1, move2)
  @rules[move1].include?(move2)
end

def score(move1, move2)
  if move1 == move2
    0
  elsif defeats?(move1, move2)
    1
  else
    -1
  end
end

def move
  player, opponent = ARGV

  case player.to_s.size
  # Throw six lizards in the beginning to confuse opponent
  when 0..5
    'L'
  when 6
    'V'
  when 7
    'S'
  when 8
    'P'
  when 9
    'R'
  else
    analyze_history(player.chars.to_a, opponent.chars.to_a)
  end

end

def analyze_history(player, opponent)
  my_last_move = player.last
  predicted_moves = Hash.new {0}
  opponent_reactions = player.zip(opponent.drop(1))

  # Check whether opponent tended to make a move that would've beaten, lost, or tied my last move
  opponent_reactions.each do |my_move, reaction|
    score = score(reaction, my_move)
    analogous_moves = @moves.select { |move| score == score(move, my_last_move) }
    analogous_moves.each { |move| predicted_moves[move] += 1 }
  end

  # Assume if an opponent has never made a certain move, it never will
  @moves.each { |m| predicted_moves[m] = 0 unless opponent.include?(m) }

  # Pick the move with the best score against opponent's possible moves, weighted by their likelihood, picking randomly for ties
  @moves.shuffle.max_by{ |m| predicted_moves.map { |predicted, freq| score(m, predicted) * freq }.reduce(0,:+) }

end

puts move

Assumes the opposing bot is always reacting to my previous move, and either picking something that would beat it, something that would lose to it, or the same move, possibly from a restricted set of possible moves. It then picks the best move given that assumption.

Except that the first ten moves are hardcoded: first I pretend I only know lizard, then I assume my opponent always throws something to beat the last thing I threw until I have enough data for proper analysis.

share|improve this answer

Java - SelfLoathingBot

BotName: SelfLoathingBot
Compile: Save as 'SelfLoathingBot.java'; compile.
Run:     java SelfLoathingBot [me] [them]

Bot starts randomly, then ~33% to go random, or ~33% to play a winning tactic against either of the immediately prior plays, with 50% choice of winning tactic.

import java.util.Random;

public class SelfLoathingBot {

    static final Random RANDOM = new Random();

    private static char randomPlay() {

        switch (RANDOM.nextInt(5)) {

            case 0 : return 'R';

            case 1 : return 'P';

            case 2 : return 'S';

            case 3 : return 'L';

            default : return 'V';
        }
    }

    private static char antiPlay(String priorPlayString) {

        char[] priorPlays = priorPlayString.toCharArray();

        int choice = RANDOM.nextInt(2);

        switch (priorPlays[priorPlays.length - 1]) {

            case 'R' : return choice == 0 ? 'P' : 'V'; 

            case 'P' : return choice == 0 ? 'S' : 'L';

            case 'S' : return choice == 0 ? 'V' : 'R';

            case 'L' : return choice == 0 ? 'R' : 'S';

            default : return choice == 0 ? 'L' : 'P'; // V        
        }
    }

    public static void main(String[] args) {

        int choice = args.length == 0 ? 0 : RANDOM.nextInt(3);

        char play;

        switch (choice) {

            case 1 :

                // 33.3% chance Play myself
                play = antiPlay(args[0]);
                break;

            case 2 :

                // 33.3% chance Play opponent just in case opponent is screwy like that
                play = antiPlay(args[1]);
                break;

            default :

                // 33.3% chance 100% Random
                play = randomPlay();
        }

        System.out.print(play);
        return;
    }
}
share|improve this answer

The Analyst

The analyst analyzes some stuff and does some things to try to beat you.

compile with javac Analyst.java and run as java Analyst

import java.util.Random;

public class Analyst{
    public static void main(String[] args){
        char action = 'S';

        try{
            char[] enemyMoves = null, myMoves = null;

            //first move is random
            if(args.length == 0){
                System.out.print(randomMove());
                System.exit(0);
            //moves 2-3 will beat their last move
            }else if(args[0].length() < 8){
                System.out.print(counterFor(args[1].charAt(args[1].length()-1)));
                System.exit(0);
            //following moves will execute some analyzation stuff
            }else{
                //get previous moves
                myMoves = args[0].toCharArray();
                enemyMoves = args[1].toCharArray();
            }

            //test if they're trying to beat our last move
            if(beats(enemyMoves[enemyMoves.length-1], myMoves[myMoves.length-2])){
                action = counterFor(counterFor(myMoves[myMoves.length-1]));
            }
            //test if they're copying our last move
            else if(enemyMoves[enemyMoves.length-1] == myMoves[myMoves.length-2]){
                action = counterFor(myMoves[myMoves.length-1]);
            }
            //else beat whatever they've done the most of
            else{
                action = counterFor(countMost(enemyMoves));
            }

            //if they've beaten us for the first 40 moves, do the opposite of what ive been doing
            if(theyreSmarter(myMoves, enemyMoves)){
                action = counterFor(action);
            }

        //if you break my program do something random
        }catch (Exception e){
            action = randomMove();
        }

        System.out.print(action);
    }

    private static char randomMove(){
        Random rand = new Random(System.currentTimeMillis());
        int randomMove = rand.nextInt(5);

        switch (randomMove){
            case 0: return 'R';
            case 1: return 'P';
            case 2: return 'S';
            case 3: return 'L';
            default: return 'V';
        }
    }

    private static char counterFor(char move){
        Random rand = new Random(System.currentTimeMillis());
        int moveSet = rand.nextInt(2);

        if(moveSet == 0){
            switch (move){
                case 'R': return 'P'; 
                case 'P': return 'S'; 
                case 'S': return 'R'; 
                case 'L': return 'R'; 
                default: return 'P';
            }
        }else{
            switch (move){
                case 'R': return 'V'; 
                case 'P': return 'L'; 
                case 'S': return 'V'; 
                case 'L': return 'S'; 
                default: return 'L';
            }
        }
    }

    private static boolean beats(char move1, char move2){
        if(move1 == 'R'){
            if((move2 == 'S') || (move2 == 'L')){
                return true;
            }else{
                return false;
            }
        }else if(move1 == 'P'){
            if((move2 == 'R') || (move2 == 'V')){
                return true;
            }else{
                return false;
            }
        }else if(move1 == 'S'){
            if((move2 == 'L') || (move2 == 'P')){
                return true;
            }else{
                return false;
            }
        }else if(move1 == 'L'){
            if((move2 == 'P') || (move2 == 'V')){
                return true;
            }else{
                return false;
            }
        }else{
            if((move2 == 'R') || (move2 == 'S')){
                return true;
            }else{
                return false;
            }
        }
    }

    private static char countMost(char[] moves){
        int[] enemyMoveList = {0,0,0,0,0};

        for(int i=0; i<moves.length; i++){
            if(moves[i] == 'R'){
                enemyMoveList[0]++;
            }else if(moves[i] == 'P'){
                enemyMoveList[1]++;
            }else if(moves[i] == 'S'){
                enemyMoveList[2]++;
            }else if(moves[i] == 'L'){
                enemyMoveList[3]++;
            }else if(moves[i] == 'V'){
                enemyMoveList[4]++;
            }
        }

        int max = 0, maxIndex = 0;
        for(int i=0; i<5; i++){
            if(enemyMoveList[i] > max){
                max = enemyMoveList[i];
                maxIndex = i;
            }
        }

        switch (maxIndex){
            case 0: return 'R';
            case 1: return 'P';
            case 2: return 'S';
            case 3: return 'L';
            default: return 'V';
        }
    }

    private static boolean theyreSmarter(char[] myMoves, char[] enemyMoves){
        int loseCounter = 0;

        if(enemyMoves.length >= 40){
            for(int i=0; i<40; i++){
                if(beats(enemyMoves[i],myMoves[i])){
                    loseCounter++;
                }
            }
        }else{
            return false;
        }

        if(loseCounter > 20){
            return true;
        }else{
            return false;
        }
    }
}
share|improve this answer

The Gambler - Python 2

import sys
import random

MODE = 1

moves = 'RSLPV'

def element_sums(a, b):
    return [a[i] + b[i] for i in xrange(len(a))]

def move_scores(p):
    def calc(to_beat):
        return ['LDW'.find('DLLWW'[moves.find(m)-moves.find(to_beat)]) for m in moves]

    return dict(zip(moves, element_sums(calc(p[0]), calc(p[1]))))

def move_chooser(my_history, opponent_history):
    predict = sorted(moves, key=opponent_history.count, reverse=MODE)[-2:]
    scores = move_scores(predict)
    return max(scores, key=lambda k:scores[k])

if __name__ == '__main__':
    if len(sys.argv) == 3:
        print move_chooser(*sys.argv[1:])
    elif len(sys.argv) == 1:
        print random.choice(moves)

Contrary to the name, the only time randomness is used in this program is on the first round, when there's no information. Instead, it's named for the gambler's fallacy, the belief that if a random event has happened less often in the past, it's more likely to happen in the future. For example, if you flip a fair coin 20 times, and the first 15 are heads, the gambler's fallacy states that the odds of the remaining flips being tails are increased. Of course, this is untrue; regardless of the previous flips, a fair coin's odds of coming up tails is always 50%.

This program analyzes the opponent's history, finds the 2 moves that it has used the least so far, and assumes that the opponent's move this time will be one of those two. Assigning 2 to a win, 1 to a draw and 0 to a loss, it finds the move with the maximum score against these two predicted moves and throws that.

The Gambler's Brother - Python 2

import sys
import random

MODE = 0

moves = 'RSLPV'

def element_sums(a, b):
    return [a[i] + b[i] for i in xrange(len(a))]

def move_scores(p):
    def calc(to_beat):
        return ['LDW'.find('DLLWW'[moves.find(m)-moves.find(to_beat)]) for m in moves]

    return dict(zip(moves, element_sums(calc(p[0]), calc(p[1]))))

def move_chooser(my_history, opponent_history):
    predict = sorted(moves, key=opponent_history.count, reverse=MODE)[-2:]
    scores = move_scores(predict)
    return max(scores, key=lambda k:scores[k])

if __name__ == '__main__':
    if len(sys.argv) == 3:
        print move_chooser(*sys.argv[1:])
    elif len(sys.argv) == 1:
        print random.choice(moves)

By switching the MODE variable to 0, this program will operate based on a related fallacy, also sometimes referred to as the gambler's fallacy. It states that if a random event has been happened more often in the past, it's more likely to happen in the future. For example, if you flip a coin 20 times and the first 15 are heads, this fallacy states that the remaining flips are more likely to be heads, since there's currently a streak. In mode 0, this program operates the same way, except that it assumes the opponent will throw one of the two moves it's thrown most often so far.

So yes, these two programs are only one character apart. :)

share|improve this answer
    
On what condition does TheGambler change MODE? –  Dr R Dizzle Jul 29 at 14:50
    
@DrRDizzle It doesn't, it looks like this is a submit of two bots in one. –  Paŭlo Ebermann Jul 29 at 16:17
1  
Would this program not be more effective if MODE switched if you lose more than a certain amount of times in a row? –  Dr R Dizzle Jul 31 at 9:00

Bash Rocks

Is cygwin too much to ask as a runtime?

bashrocks.sh:

#!/bin/bash
HAND=(R P S L V)
RAND=`od -A n -t d -N 1 /dev/urandom | xargs`
echo ${HAND[ $RAND  % 5 ]}

and run it like so:

sh bashrocks.sh
share|improve this answer
4  
After reading the title, I'm slightly disappointed that you do anything but R. ;) –  Martin Büttner Jul 25 at 16:53
    
@mccannf. having some problems with this one... I've installed cygwin and modified your scripts with fully qualified paths to C:\Cygwin\bin for od.exe, xargs.exe & echo.exe. still getting the following error. C:/Cygwin/bin/xargs: echo: No such file or directory % 5 ")syntax error: operand expected (error token is " –  Eoin Campbell Jul 27 at 20:44
    
@EoinCampbell - when you create the file in windows, can you then run dos2unix on the file in cygwin before executing it? –  mccannf Jul 27 at 21:00
    
sure. I'll give that a try. –  Eoin Campbell Jul 27 at 21:13
    
I think the problem might be with the /dev/urandom statement –  Eoin Campbell Jul 27 at 21:20

Algorithm

An algorithm for the sake of having one.

Cuz' it always feels safer doing something, the more complicated the better.

Haven't done some serious Math yet, so this algorithm may not be that effective.

import random, sys

if __name__ == '__main__':

    # Graph in adjacency matrix here
    graph = {"S":"PL", "P":"VR", "R":"LS", "L":"VP", "V":"SR"}
    try:
        myHistory = sys.argv[1]
        opHistory = sys.argv[2]
        choices = ""

        # Insert some graph stuff here. Newer versions may include advanced Math.
        for v in graph:
            if opHistory[-1] == v:
                for u in graph:
                    if u in graph[v]:
                        choices += graph[u]

        print random.choice(choices + opHistory[-1])

    except:
        print random.choice("RPSLV")

Python 2 program: python algorithm.py

share|improve this answer
1  
Summary of this algorithm: look at what the opponent last played, and then randomly play one of the two moves that would lose against the opponent's last move if they played it again. So it's better against bots that don't play the same move twice in a row. –  Rory O'Kane Jul 26 at 4:23
    
Haha. I don't really know if I did made it that way. If I am not wrong, it is actually just a convoluted way of randomly selecting any of the 5 moves. ;) –  bitpwner Jul 26 at 6:35

FairBot, Ruby

Let's start simple.

puts ['R','P','S','L','V'].sample

Run like

ruby fairbot.rb
share|improve this answer
    
small typo on that last 'V' param. have fixed it on myside if you wanna update for completeness –  Eoin Campbell Jul 25 at 18:50
    
@EoinCampbell thanks, fixed! –  Martin Büttner Jul 25 at 18:56
1  
The interesting thing is that this has exactly equal odds of winning against ALL strategies. –  Cruncher Jul 29 at 18:58

ViolentBot

This bot choices the most violent option based on the opponents previous choice:

import sys

choice_dict = {"L" : "S", "P" : "S", "R" : "V", "S" : "V", "V" : "L"}

total = len(sys.argv)
if total==1:
    print("L")
    sys.exit()

opponent = sys.argv[2]
opponent_last = opponent[-1]

print(choice_dict[opponent_last])

Run as

python ViolentBot.py (me) (opp)
share|improve this answer
    
breaks with no params. Traceback (most recent call last): File "ViolentBot\ViolentBot.py", line 9, in <module> opponent = sys.argv[2] IndexError: list index out of range –  Eoin Campbell Jul 25 at 19:26
    
breaks with params. Traceback (most recent call last): File "ViolentBot\ViolentBot.py", line 12, in <module> print(choice_dict[opponent_last]) KeyError: 'S' –  Eoin Campbell Jul 25 at 19:27
    
@EoinCampbell: I've added an exit clause for the first run, you should be able to run it now. –  Kyle Kanos Jul 25 at 19:55

Werevulcan - Ruby

Run as ruby werevulcan.rb

@rules = {

  'L' => %w[V P],
  'P' => %w[V R],
  'R' => %w[L S],
  'S' => %w[P L],
  'V' => %w[R S]
}

@moves = @rules.keys

def defeats?(move1, move2)
  @rules[move1].include?(move2)
end

def score(move1, move2)
  if move1 == move2
    0
  elsif defeats?(move1, move2)
    1
  else
    -1
  end
end

def move
  player, opponent = ARGV

  # For the first 30 rounds, pick a random move that isn't Spock
  if player.to_s.size < 30
    %w[L P R S].sample
  elsif opponent.chars.to_a.uniq.size < 5
    exploit(opponent)
  else
    # Pick a random move that's biased toward Spock and against lizards
    %w[L P P R R S S V V V].sample
  end

end

def exploit(opponent)
  @moves.shuffle.max_by{ |m| opponent.chars.map{|o| score(m,o) }.reduce(:+) }
end

puts move

The werevulcan looks normal by day, but when the moon rises, its ears grow pointy, and its moves grow more logical.

share|improve this answer

Haskell - MonadBot

I don't know if ghc is considered "within reason", but let's just assume it is. This bot's strategy is to counter its opponent's most popular move.

Compile: ghc monadbot.hs
Run:     ./monadbot [Arg1] [Arg2]

Code:

import System.Environment
import Data.List
import Data.Ord

main :: IO ()
main = do
  args <- getArgs
  let moves = if not (null args) then args !! 1 else ""
      fave = if not (null moves) then head $ maximumBy (comparing length) (group $ sort moves) else 'V'
  putChar $ case fave of 'R' -> 'P'
                         'P' -> 'S'
                         'S' -> 'R'
                         'L' -> 'R'
                         'V' -> 'P'
                         _   -> 'V'
share|improve this answer

SuperMarkov, Python

An improved version of the current Markov bot. Makes chains of length 0, 1, and 2, using either only our moves, only his moves, or both, to predict. Weighs the chains and picks what it expects the best result to be. Takes the result less seriously if the sample size is small.

Bot: SuperMarkov
Run: python SuperMarkov.py [Arg1] [Arg2]

Code:

import pprint
import sys
import random

if len(sys.argv) < 2:
    our_hist = opp_hist = "x"
else:
    our_hist = 'x'+sys.argv[1]
    opp_hist = 'x'+sys.argv[2]

hist = zip(opp_hist, our_hist)

def build_chains(length):
    chains = {}
    for typ in ('ours', 'theirs', 'both'):
        chain = {}
        for i in xrange(0, len(hist)-length):
            if typ == 'ours':
                prev = our_hist[i:i+length]
            elif typ == 'theirs':
                prev = opp_hist[i:i+length]
            elif typ == 'both':
                prev = tuple(hist[i:i+length])
            else:
                raise ValueError()
            next = opp_hist[i+length]
            if next == 'x':
                continue

            chain.setdefault(prev, {})
            if next not in chain[prev]:
                chain[prev][next] = 1
            else:
                chain[prev][next] += 1

        #normalize the chain
        # for prev in chain:
        #     total = sum(v for v in chain[prev].values())
        #     for next in list(chain[prev]):
        #         chain[prev][next] /= float(total)

        chains[typ] = chain

    return chains

beats = {
    "S": "LP",
    "P": "VR",
    "R": "SL",
    "L": "VP",
    "V": "SR",
}
beaten_by = {
    "S": "VR",
    "P": "SL",
    "R": "PV",
    "L": "SR",
    "V": "PL",
}

weighted_evs = dict((c, 0) for c in "SPRLV")
chain_weights = {0: 1, 1: 2, 2: 4}
chain_type_weights = {'theirs': 1, 'ours': 1, 'both': 3}

for L in (0, 1, 2):
    chains = build_chains(L)
    for typ, chain in chains.items():
        if typ == 'ours':
            this_prev = our_hist[-L:] if L > 0 else ''
        elif typ == 'theirs':
            this_prev = opp_hist[-L:] if L > 0 else ''
        elif typ == 'both':
            this_prev = tuple(hist[-L:]) if L > 0 else ()

        this_nexts = chain.get(this_prev, {})
        #print typ, this_prev, this_nexts
        sample_size = sum(v for v in this_nexts.values())
        if sample_size == 0:
            continue

        this_weight = chain_weights[L] * chain_type_weights[typ]
        if sample_size < 4:
            this_weight /= 4.0 - sample_size

        for choice in "SVPLR":
            ev = 0
            for which, count in this_nexts.items():
                if which == choice:
                    # push
                    continue
                elif which in beats[choice]:
                    ev += float(count)/sample_size
                elif which in beaten_by[choice]:
                    ev -= float(count)/sample_size
                else:
                    raise ValueError()
            #print "According to %s of length %d, picking %s gives EV %.3f" % (typ, L, choice, ev)
            weighted_evs[choice] += ev * this_weight

#pprint.pprint(weighted_evs)
print max("SPRLV", key=lambda choice: weighted_evs[choice])
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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