98
\$\begingroup\$

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.

\$\endgroup\$
  • 7
    \$\begingroup\$ What does the history look like when a player has forfeited a hand? \$\endgroup\$ – histocrat Jul 25 '14 at 17:41
  • 1
    \$\begingroup\$ I was going to go all-out with an analytic approach, but most of the bots here are stupid enough to defeat smart AI. \$\endgroup\$ – fluffy Jul 25 '14 at 21:23
  • 1
    \$\begingroup\$ Just because I never am in the lead for any KotH challenge I've competed in, I've taken a screenshot as a memento. \$\endgroup\$ – Kyle Kanos Jul 26 '14 at 2:55
  • 3
    \$\begingroup\$ 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 \$\endgroup\$ – Eoin Campbell Jul 28 '14 at 7:55
  • 3
    \$\begingroup\$ 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.) \$\endgroup\$ – Emil Jul 29 '14 at 7:07

79 Answers 79

1
\$\begingroup\$

Naan (Ruby)

BotName: Naan
Run:     ruby naan.rb [Mine] [Theirs]

Code

@counters = { "R" => [ "P", "V" ], "P" => [ "S", "L" ], "S" => [ "V", "R" ], "L" => [ "R", "S" ], "V" => [ "P", "L" ] }
@counts = { "R" => 0, "S" => 0, "P" => 0, "V" => 0, "L" => 0 }

choices = []

if( ARGV != nil && ARGV[0] != nil )

  mine = ARGV[0].split("")
  theirs = ARGV[1].split("")

  c2 = @counts.clone
  bestChoice = 100

  mine.each_cons(2) {|a, b| if( a == mine[-1] ) then c2[b]+=1 end}
  c2.keys.each {|x| if( c2[x] < bestChoice ) then bestChoice = c2[x] end}
  c2.keys.each {|x| if( c2[x] == bestChoice ) then choices.push(x) end}

else
  choices = @counters.keys
end

puts choices.sample
\$\endgroup\$
1
\$\begingroup\$

NaanViolence (Ruby)

BotName: NaanViolence
Run:     ruby naanviolence.rb [Mine] [Theirs]

Code

@counters = { "R" => [ "P", "V" ], "P" => [ "S", "L" ], "S" => [ "V", "R" ], "L" => [ "R", "S" ], "V" => [ "P", "L" ] }
@counts = { "R" => 0, "S" => 0, "P" => 0, "V" => 0, "L" => 0 }

choices = []

if( ARGV != nil && ARGV[0] != nil )

  mine = ARGV[0].split("")
  theirs = ARGV[1].split("")

  c2 = @counts.clone
  bestChoice = 100

  mine.each {|x| c2[x]+=2}
  theirs.each {|x| c2[@counters[x][0]]-=1; c2[@counters[x][1]]-=1}
  c2.keys.each {|x| if( c2[x] < bestChoice ) then bestChoice = c2[x] end}
  c2.keys.each {|x| if( c2[x] == bestChoice ) then choices.push(x) end}

else
  choices = @counters.keys
end

puts choices.sample
\$\endgroup\$
1
\$\begingroup\$

OneBehind, Java

Always one behind... T^T

public class OneBehind {

    public static void main(String[] args) {
        if(args.length == 0) {
            System.out.print("L"); //Represent
            return;
        }
        char[] opp = args[1].toCharArray();
        int index = opp.length - 1;
        char choice = opp[index];
        switch(choice) {
            case 'R':
            System.out.print("R"); 
            break;
            case 'P':
            System.out.print("P");  
            break;
            case 'S':
            System.out.print("S");
            break;
            case 'L':
            System.out.print("L");
            break;
            case 'V':
            System.out.print("V");
            break;
        }
        return;
    }

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

EasyGame, Java

My try at this tournament

Bot: EasyGame
Run: java -jar EasyGame.jar [Arg1] [Arg2]

Code:

public class EasyGame {

    public static String randomLetter(){
        String[] randomLetters = new String[]{"R","P","S","L","V"};
        String character = (randomLetters[(int)Math.floor(Math.random()*5)]);
        return character;
    }

    public static int[] calculateScores(int[] scores, char[] enemyPlays){
        for (int i=0; i != enemyPlays.length; i++){
            if (enemyPlays[i] == 'R') scores[0]++;
            if (enemyPlays[i] == 'P') scores[1]++;
            if (enemyPlays[i] == 'S') scores[2]++;
            if (enemyPlays[i] == 'L') scores[3]++;
            if (enemyPlays[i] == 'V') scores[4]++;
        }
        return scores;
    }

    public static boolean randomPick(int chances, int size){
        if (chances >= (int)Math.floor(Math.random()*size))
            return true;
        return false;       
    }

    public static char nextMove(int[] seed, int length){
        if (randomPick(seed[0], length)) return 'P';
        if (randomPick(seed[1], length-(seed[0]))) return 'S';
        if (randomPick(seed[2], length-(seed[0]+seed[1]))) return 'V';
        if (randomPick(seed[3], length-(seed[0]+seed[1]+seed[2]))) return 'R';
        if (randomPick(seed[4], length-(seed[0]+seed[1]+seed[2]+seed[3]))) return 'L';
        return 'V';
    }

    public static void main(String[] args) {

        int[] scores = new int[]{0,0,0,0,0};
        if(args.length == 0 || args[1].length() < 10){
            System.out.print(randomLetter());
            return;
        }

        char[] enemyPlays = args[1].toCharArray();
        scores = calculateScores(scores, enemyPlays);
        System.out.print(nextMove(scores, args[1].length()));
        return;
    }
}

This bot picks a seeded randomized option, based on the moves made by the opponent.

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

The Bot You Could Easily Special Case Against - Python 2

Plays a fixed game that I wrote by hand.

import sys

game = "RPSPVPSLSRVVVSVVSLLPPRSLPRRRRVRLLPSSRPRPRSPRPRLLLVSRPSSPRVVRPVVVPPVRRLLLLLRLRPVVSSPRRPSLVLRRRSPRSRRP"

try:
    round = len(sys.argv[1])
except IndexError:
    round = 0

print game[round]

Run with:

python TheBotYouCouldEasilySpecialCaseAgainst.py <args>

Giving your grid formatter a workout.

\$\endgroup\$
  • \$\begingroup\$ Renamed OboeBeater \$\endgroup\$ – Eoin Campbell Jul 30 '14 at 19:55
  • \$\begingroup\$ This is the only bot in the field so far that consistently beats my bot Pony. :) \$\endgroup\$ – Emil Jul 31 '14 at 12:09
  • \$\begingroup\$ Hahaha. xD Well you could easily special case it. \$\endgroup\$ – Docopoper Jul 31 '14 at 12:50
  • \$\begingroup\$ @Pony any idea how it would perform against a bot which was making it's decisions against something like the Linus Sequence ? \$\endgroup\$ – Eoin Campbell Aug 1 '14 at 8:18
1
\$\begingroup\$

The Bot That Easily General Cases The Bot You Could Easily Special Case Against - Python 2

*BotName: OboeOboeBeater *

Plays a fixed game that beats the game I wrote by hand.

import sys

game = "VLVSLLVSRPLPPRLPRSRSSPRSLPPVPPVSSSVRPSPSPRLPLPRRSPVVLRRSPLPVSPLLSLPPVRSSRRVRVSLLRVLVPSVSLRVVPVSPRVVL"

try:
    round = len(sys.argv[1])
except IndexError:
    round = 0

print game[round]

Run with:

python TheBotThatEasilyGeneralCasesTheBotYouCouldEasilySpecialCaseAgainst.py <args>

Formatting! :D

\$\endgroup\$
  • \$\begingroup\$ renamed OboeOboeBeater :-) \$\endgroup\$ – Eoin Campbell Jul 30 '14 at 19:50
  • \$\begingroup\$ Hahaha. Though it doesn't beat RoboticOboeBotOboeTuner, it beats TheBotYouCouldEasilySpecialCaseAgainst. \$\endgroup\$ – Docopoper Jul 30 '14 at 21:40
1
\$\begingroup\$

SmarterBot

This one might be smarter.

It has a few more features than its predecessor, including optimizations such as:

  • Choosing a move now takes into account the runner up - so that the next move will (some of the time) beat both
  • Outputs random move if beaten twice in a row
  • If beaten twice in a row, history is effectively cleared for the next hands

    name - SmarterBot
    run with - java SmarterBot.jar
    

code:

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

public class SmarterBot {

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;
    String myUsableHistory = myHistory;
    String otherUsableHistory = otherHistory;
    if(myHistory.length() > 4){
        int startIndex = findLastInstanceOfXLosses(myHistory,otherHistory,3);
        myUsableHistory = myHistory.substring(startIndex);
        otherUsableHistory = otherHistory.substring(startIndex);
    }

    if(lostLastXRounds(myHistory,otherHistory) >= 1){
        String[] randLetter = new String[]{"R","P","S","L","V"};
        System.out.print(randLetter[(int) Math.floor(Math.random()*5)]);
        return;
    }

    ArrayList<ArrayList<Integer>> moveHits = new ArrayList<ArrayList<Integer>>();
    for(int g = 0;g<2;g++){
        for(int i=1;i<(myUsableHistory.length() / 2) + 1;i++){
            if(g==0){
                moveHits.add(findAll(myUsableHistory.substring(myUsableHistory.length() - i),myUsableHistory));
            }
            else{
                moveHits.add(findAll(otherUsableHistory.substring(otherUsableHistory.length() - i),otherUsableHistory));
            }
        }
        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<= otherUsableHistory.length()){
                    char nextMove = otherUsableHistory.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;}
                }
            }
        }
    }

    HashMap<Character,Double> scores = new HashMap<Character,Double>();
    scores.put('R', rScore);
    scores.put('P',pScore);
    scores.put('S', sScore);
    scores.put('L', lScore);
    scores.put('V', vScore);
    ArrayList<Character> winners = orderHashMap(scores);
    char number1 = winners.get(winners.size() - 1);
    char number2 = winners.get(winners.size() - 2);
    char charToPlay = beatsMove(number1,number2);
    System.out.print(charToPlay);
    return;
}
public static int findLastInstanceOfXLosses(String myHistory,String otherHistory, int losses){
    int index = myHistory.length();
    while(lostLastXRounds(myHistory.substring(0,index),otherHistory.substring(0,index)) < losses && index >1){
        index--;
    }
    return index;
}
public static ArrayList<Character> orderHashMap(HashMap<Character,Double> scores){
    ArrayList<Double> orderedScores = new ArrayList<Double>();
    orderedScores.add(scores.get('R'));
    orderedScores.add(scores.get('P'));
    orderedScores.add(scores.get('S'));
    orderedScores.add(scores.get('L'));
    orderedScores.add(scores.get('V'));
    Collections.sort(orderedScores);

    ArrayList<Character> orderedKeys = new ArrayList<Character>();
    for(Double value:orderedScores){
        for(Character key:scores.keySet()){
            if(value.equals(scores.get(key))){
                orderedKeys.add(key);
            }
        }
    }
    return orderedKeys;
}
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;
}
public static int lostLastXRounds(String myHistory,String otherHistory){
    int index = myHistory.length()-1;
    int lost = 0;
    while(ifCharBeatsOther(otherHistory.charAt(index),myHistory.charAt(index)) == true){
        index--;
        lost++;
        if(index == -1){break;}
    }
    return lost;
}
public static boolean ifCharBeatsOther(char char1,char char2){
    ArrayList<Character> beatsRock = new ArrayList<Character>();
    beatsRock.add('V');
    beatsRock.add('P');
    ArrayList<Character> beatsPaper = new ArrayList<Character>();
    beatsPaper.add('S');
    beatsPaper.add('L');
    ArrayList<Character> beatsSissors = new ArrayList<Character>();
    beatsSissors.add('V');
    beatsSissors.add('R');
    ArrayList<Character> beatsLizard = new ArrayList<Character>();
    beatsLizard.add('S');
    beatsLizard.add('R');
    ArrayList<Character> beatsSpock = new ArrayList<Character>();
    beatsSpock.add('P');
    beatsSpock.add('L');

    if(char2 == 'V'){if (beatsSpock.contains(char1)){return true;}}
    if(char2 == 'R'){if (beatsRock.contains(char1)){return true;}}
    if(char2 == 'P'){if (beatsPaper.contains(char1)){return true;}}
    if(char2 == 'S'){if (beatsSissors.contains(char1)){return true;}}
    if(char2 == 'L'){if (beatsLizard.contains(char1)){return true;}}
    return false;
}
public static char beatsMove(char moveToBeBeaten,char runnerUp){
    if(moveToBeBeaten == 'R'){
        if(runnerUp == 'S'){
            return 'V';
        }
        if(runnerUp == 'V'){
            return 'P';
        }
        return 'V';
    }
    if(moveToBeBeaten == 'P'){
        if(runnerUp == 'L'){
            return 'S';
        }
        if(runnerUp == 'V'){
            return 'L';
        }
        return 'L';
    }
    if(moveToBeBeaten == 'S'){
        if(runnerUp == 'R'){
            return 'V';
        }
        if(runnerUp =='L'){
            return 'R';
        }
        return 'V';
    }
    if(moveToBeBeaten == 'L'){
        if(runnerUp == 'P'){
            return 'S';
        }
        if(runnerUp == 'S'){
            return 'R';
        }
        return 'S';
    }
    if(moveToBeBeaten == 'V'){
        if(runnerUp == 'R'){
            return 'P';
        }
        if(runnerUp == 'P'){
            return 'L';
        }
    }
    return 'L';
}
}

It has beaten SmartBot more than 50% of the time so it could do quite well.

Or it could crash and burn.

\$\endgroup\$
  • \$\begingroup\$ Some bugs popped up in the last tourney for your bot. Have a look in the log URL at the top and search for the game "SelfHatingBot & SmarterBot". Looks like your bot started spitting out blank strings and auto-losing rounds after SHB played the sequence "RVPLRP" \$\endgroup\$ – Eoin Campbell Jul 30 '14 at 13:13
1
\$\begingroup\$

Blind Foresight - c#

The obvious flaws of this bot are its strengths.

Bot    : Blind Foresight
Compile: csc BlindForesight.cs
Run    : BlindForesight [Arg1] [Arg2]

Code:

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace SO.BlindForesight
{

    /// <summary>
    /// Blind Foresight
    /// </summary>
    class Program
    {
        public enum Hand
        {
            Rock,
            Paper,
            Scissors,
            Vulcan,
            Lizard
        }

        public enum Outcome
        {
            Win=1,
            Tie=0,
            Loss=-1,
        }

        static Random rnd=new Random();
        static Dictionary<char, Hand> interpreter;
        static Program()
        {
            interpreter=new Dictionary<char, Hand>();
            foreach(var item in Enum.GetValues(typeof(Hand)))
            {
                Hand hand=(Hand)item;
                char key=hand.ToString()[0];
                interpreter[key]=hand;
            }
        }

        public struct Game
        {
            public Game(Hand mine, Hand other)
            {
                this.mine=mine;
                this.other=other;
                this.result=mine==other?Outcome.Tie:
                    ((int)mine+2)%5==(int)other||((int)mine+4)%5==(int)other?Outcome.Win:
                        Outcome.Loss;
            }
            public Game(char mine, char other) : this(interpreter[mine], interpreter[other]) { }
            public readonly Hand mine, other;
            public readonly Outcome result;
        }

        static void Main(string[] args)
        {
            if(args.Length==0) { Return(rnd.Next(0, 5)); }
            else if(args.Length==1) { throw new ArgumentException(); }
            else
            {
                int n=args[0].Length;
                Game last=new Game(args[0][n-1], args[1][n-1]);
                if(last.result==Outcome.Loss)
                {
                    Return((int)last.other+rnd.Next(0, 2)*2-1);
                }
                else
                {
                    Return((int)last.mine+rnd.Next(0, 2)*2-1);
                }
            }
        }
        static void Return(int hand)
        {
            if(hand<0) { hand+=5*(1-hand/5); }
            Console.WriteLine(((Hand)(hand%5)).ToString()[0]);
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ this bot is broken. args[0] won't exist on the first iteration. \$\endgroup\$ – Eoin Campbell Jul 30 '14 at 21:12
  • \$\begingroup\$ Good catch. I fixed it. \$\endgroup\$ – ja72 Jul 30 '14 at 22:19
1
\$\begingroup\$

Leonard Shelby

Hey, I removed one so I can add one, right? ruby leonard.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

  hardcoded_moves = 'PRPSPLPVPRPSPLPV'

  if m = hardcoded_moves[player.to_s.size]
    m
  elsif winning?(player, opponent)
    %w[R P S L V].sample
  else
    extrapolate(player[-16,16],opponent[-16,16])
  end

end

def winning?(player,opponent)
  player.chars.zip(opponent.chars).map{ |move| score(*move) }.inject(0,:+) >= 5
end

def extrapolate(player,opponent)
  likelihoods = Hash.new {0}

  opponent_last_move = opponent[-1]
  @moves.each { |m| likelihoods[m] += opponent.scan(opponent_last_move + m).size }

  my_last_move = player[-1]
  reactions = player.chars.zip(opponent.chars.drop(1))
  @moves.each { |m| likelihoods[m] += reactions.count([my_last_move, m]) }

  @moves.shuffle.max_by do |m|
    likelihoods.map{ |n,c| score(m,n) * c }.reduce(0,:+)
  end

end

puts move

Leonard needs a lot of paper because he can't really remember more than the recent (16 rounds) past.

This bot uses a few counter-intuitive ideas. One is that it switches to fully random moves when it's winning, just in case the opposing bot is on the verge of figuring out its strategy. It'll have fewer lopsided victories, but hopefully more victories overall.

\$\endgroup\$
  • \$\begingroup\$ yup. included in next run. \$\endgroup\$ – Eoin Campbell Jul 31 '14 at 17:35
1
\$\begingroup\$

RAMBO_BOT - PHP

introducing brand new Rambo Bot

fight it by run php 5.4.*

php rambo_bot.php

e.g

php rambo_bot.php SSVL VLSS

i dont know what exactly i did, things got bit obfuscating but my plan was to make it smart, should burst those spammer bots

Update :

from public aplpha to public beta, new features included, better narrow down and dependencies, it got lot more intelligent

<?php
/* RamboBot public beta */
class V{
    protected $protected;
    function p(){
        return [get_class($this->protected),"P"];
    }
}


function generateKnowledge($l,$o){

    $j = generateResist();
    $knowledge = [ $j[0][0], $j[1][0], $j[1][1], $j[2], $j[4], mt_rand(0,1), (100/3) ];
    if($l[0] != ""){
        $am = [
            $j[0][0]=>[count(array_keys($l, $j[0][0])),count(array_keys($o, $j[0][0]))],$j[1][0]=>[count(array_keys($l, $j[1][0])),count(array_keys($o, $j[1][0]))],$j[1][1]=>[count(array_keys($l, $j[1][1])),count(array_keys($o, $j[1][1]))],$j[2]=>[count(array_keys($l, $j[2])),count(array_keys($o, $j[2]))],$j[4]=>[count(array_keys($l, $j[4])),count(array_keys($o, $j[4]))]
        ];
        $ma = [];
        $y=0;
        foreach ($am as $key => $value) {
            $ma[]=round(( $value[1]/count($o)) * 100 );
            $y+=round(( $value[1]/count($o)) * 100 );
        }
        $mu=$ma[(array_search(max($ma), $ma))];
        $me=($y / count($am));
        $mo=[];
        foreach ($ma as $key => $value) {
            if($value==0.0){$mo[]=$key;}
        }
        $i=$knowledge;
        unset($i[5]);unset($i[6]);
        foreach ($mo as $no) {
            unset($i[$no]);
        }
        if(($mu-$me)>$knowledge[6]){            
            $e=array_search(max($ma), $ma);
        }elseif (count($o) > $knowledge[6]) {
            if(count($mo)>0){
                $e=array_rand($i);
            }else{
                $e=array_search(max($ma), $ma);
            }
        }else {
            $e=array_rand($i);
        }
    }else {
        $e=mt_rand(0,4);
    }
    return array("k"=>$knowledge,"a"=>$e);
}

function generateResist (){
    $t = (string)float;
    $f=["S",'f'];
    $b=(string)$f;
    $z=new V();$u=$z->p();$x = 'u';
    $d=$f[1];
    $resist =[$$d[0][0][0],$$x[0],ucfirst(substr($b, 2,1)),$$x[0][1][0],ucfirst(substr($t, 1,1))  ];
    return $resist;
}

$h=generateKnowledge(str_split($argv[1]),str_split($argv[2]));

switch ($h["k"][$h["a"]]) {
    case $h["k"][0]:
        echo $h["k"][3];
        exit;
    case $h["k"][1]:
        echo $h["k"][2];
        exit;
    case $h["k"][2]:
        echo $h["k"][4];
        exit;
    case $h["k"][3]:
        echo $h["k"][1];
        exit;
    case $h["k"][4]:
        echo $h["k"][3];
        exit;

    default:
        exit;
}
?>

i´m excited to see som results

rambo_bot decides depending on the length of the game if it will beat the opponents move with the highest percentual amount occured if the comparison to the average percentual amount is greater than a certain amount,

rambo_bot wont fight any moves the opponent has not been played yet starting after the first third of the whole game, if the distance between the lowest percentual amount of occurences but greater than zero and the average percentual amount of occurences is bigger than a certain amount it will exclude to beat that character

\$\endgroup\$
  • \$\begingroup\$ definitely have you in this time :-) \$\endgroup\$ – Eoin Campbell Aug 1 '14 at 16:29
  • \$\begingroup\$ yesss baby i´m excited \$\endgroup\$ – john Smith Aug 1 '14 at 18:48
1
\$\begingroup\$

RelaxedBot

The RelaxedBot doesn't worry about much. If he won last time, then he might as well try what he did again. Otherwise, something random will do just fine.

run with python RelaxedBot.py [me] [them]

import random
import sys


def pickRand():
    plays = ['R', 'P', 'S', 'L', 'V']
    return random.choice(plays)

def beat(me, them):
    if me==them:
        return 0
    if(me=='V' and (them == 'L' or them == 'P')):
        return -1
    if(me=='S' and (them == 'V' or them == 'R')):
        return -1
    if(me=='P' and (them == 'S' or them == 'L')):
        return -1
    if(me=='R' and (them == 'P' or them == 'V')):
        return -1
    if(me=='L' and (them == 'R' or them == 'S')):
        return -1
    return 1

if len(sys.argv) > 1:
    if beat((sys.argv[1])[-1], (sys.argv[2])[-1]) > 0:
        print(sys.argv[1][-1])

    else:
        print(pickRand())
else:
    print(pickRand())
\$\endgroup\$
  • \$\begingroup\$ python 2 or 3 ? \$\endgroup\$ – Eoin Campbell Jul 31 '14 at 16:03
  • \$\begingroup\$ python 3 should do it. \$\endgroup\$ – Stranjyr Jul 31 '14 at 17:32
  • 1
    \$\begingroup\$ getting an error when running it with params. are you missng a sys. on some of those argv calls. if you can get me a fix in the next 25 minutes I'll include it in this run. \$\endgroup\$ – Eoin Campbell Jul 31 '14 at 17:34
  • \$\begingroup\$ Traceback (most recent call last): File "RelaxedBot.py", line 25, in <module> if beat((argv[1])[-1], (argv[2])[-1]) > 0: NameError: name 'argv' is not defined \$\endgroup\$ – Eoin Campbell Jul 31 '14 at 17:34
  • \$\begingroup\$ Thanks for the heads up. You are really putting a lot of work into this tournament. It should be fixed now. I don't have python on this computer, so I haven't been able to test it. :/ \$\endgroup\$ – Stranjyr Jul 31 '14 at 17:39
1
\$\begingroup\$

TobiasFuenke, Python 2.7

Ugly piece of c...ake that analyzes stuff. Basically, an attempted naive Bayes classifier calculates probabilities for the opponents next move, which are used to find the own move with the best expected outcome. I might provide more information and cleaner/commented code. I probably actually will if someone requests for that.

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

Code:

import random
import sys

hands = "RPSVL"

def outcome(h1, h2):
    dist = (hands.index(h1)-hands.index(h2)) % 5
    if dist==0:
        return 0
    if dist & 1:
        return 1
    return -1

class EM():
    def __init__(self, predictor):
        self.predictor = predictor

    def hand(self, ours="", theirs=""):
        probs = self.predictor.probs(ours, theirs)

        if not(probs):
            return random.choice(hands)

        expectation = { our : sum([outcome(our, their) * probs[their] for their in hands]) for our in hands }
        winning = max(expectation.values())
        candidates = filter(lambda h: expectation[h]==winning, expectation.keys())
        return random.choice(candidates)

class Predictor:
    def __init__(self):
        self.fnum = 7

    def extract(self, ours, theirs, idx):
        if idx < 2:
            return None

        f1 = ours[idx-1]
        f2 = theirs[idx-1]
        f3 = (f1, f2)
        f4 = (f1, (f1 == ours[idx-2]))
        f5 = (f2, (f2 == theirs[idx-2]))
        f6 = (f1, ours[idx-2])
        f7 = (f2, theirs[idx-2])

        return [f1,f2,f3,f4,f5,f6,f7]

    def probs(self, ours, theirs):
        if not(theirs):
            return None

        idx = 0

        fcount = []
        flcount = []
        for i in range(0, self.fnum):
            fcount.append({})
            flcount.append({})

        lcount = {}
        samples = 0

        while idx < len(theirs):
            label = theirs[idx]

            features = self.extract(ours, theirs, idx)
            if not(features):
                idx +=1
                continue

            samples += 1 

            if not(label in lcount):
                lcount[label] = 0

                for feature in flcount:
                    feature[label] = {}

            lcount[label] += 1

            for fi in range(0, self.fnum):
                realz = features[fi]
                fcount_i = fcount[fi]

                if not(realz in fcount_i):
                    fcount_i[realz] = 0

                fcount_i[realz] += 1

                flcount_il = flcount[fi][label]
                if not(realz in flcount_il):
                    flcount_il[realz] = 0

                flcount_il[realz] += 1

            idx+=1

        if not(samples):
            return None

        lprob = {label: float(c) / samples for (label, c) in lcount.items() }
        flprob = [ { label : { realz : float(c) / sum(realizations.values()) for realz, c in realizations.items()} for label, realizations in feature.items()} for feature in flcount ]

        features = self.extract(ours, theirs, idx) 

        probs = {}
        for label in hands:
            if not(label in lprob):
                probs[label] = 0
                continue

            prob = lprob[label]
            for fi in range(0, self.fnum):
                realz = features[fi]
                flprob_il = flprob[fi][label]
                if realz in flprob_il:
                    prob *= flprob_il[realz]
                elif flprob_il:
                    prob = 0
                    break

            probs[label] = prob

        return probs

bot = EM(Predictor())

if len(sys.argv) == 3:
    move = bot.hand(sys.argv[1], sys.argv[2])
else:
    move = bot.hand()

sys.stdout.write(move)
\$\endgroup\$
  • \$\begingroup\$ pytho2 or 3 or does it matter ? \$\endgroup\$ – Eoin Campbell Aug 1 '14 at 13:34
  • \$\begingroup\$ Yes, it needs Python2. \$\endgroup\$ – ovenror Aug 1 '14 at 14:14
  • 2
    \$\begingroup\$ Oh and BTW: Thanks for your awesome game master work here :) \$\endgroup\$ – ovenror Aug 1 '14 at 14:20
1
\$\begingroup\$

Dice Throw Bot, C#

Truly Random

using System;
using Microsoft.CSharp;
using Microsoft.Win32.SafeHandles;
using System.Collections.Generic;
using System.Linq;

public static void Main (string[] args)
{
    var computerPlay = GetRandomOption(gameGestures);
    IList<Gesture> gameGestures = GetGestures();

    public static Gesture GetRandomOption (IList<Gesture> options)
    {
        Random rand = new Random();
        var returnVariable = options[rand.Next (0,options.Count)];
        return options[4];  //chosen by fair dice roll.
                            //guaranteed to be random.
    }

    // http://xkcd.com/221/

    static IList<Gesture> GetGestures()
    {
        var v = new Gesture("Spock");
        var l = new Gesture("Lizard");
        var p = new Gesture("Paper");
        var r = new Gesture("Rock");
        var s = new Gesture("Scissors");
    }

    Console.WriteLine(computerPlay.ToString());
}

public class Gesture
{
    public override string ToString()
    {
        return Name;
    }

    public Gesture(string name)
    {
        Name = name;
    }
}
\$\endgroup\$
  • \$\begingroup\$ There are already two bots with this exact strategy. From the spec: "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: [...] Pseudo Random - SimpleRandomBot & FairBot." \$\endgroup\$ – Martin Ender Aug 1 '14 at 22:23
  • 1
    \$\begingroup\$ Thank you @MartinBüttner I will edit it a little and repost! \$\endgroup\$ – Malachi Aug 1 '14 at 22:25
  • \$\begingroup\$ what Martin Said :-) If you want to change your logic slightly so it's not just a simple 20% Random bot, then you can re-submit \$\endgroup\$ – Eoin Campbell Aug 1 '14 at 22:26
  • \$\begingroup\$ Eh... I see you made some edits to this today, but did you even compile it ? it's very broken. No class around main, 2 methods inside main, no return List/Return on GetGestures, use of gameGestures variable before it exists, and at the end of the day, you're just returning option[4] making this a sciossor bot which still leaves it DQ'd :-\ sorry \$\endgroup\$ – Eoin Campbell Aug 4 '14 at 21:17
  • \$\begingroup\$ @EoinCampbell, I was talking to people earlier about that, they mentioned the Randomness point and that it's been done before, so it's essentially the same thing as Edward ScissorHands. I will see if I have some time tonight to make changes to it. I pulled this code from a Challenge on CodeReview so I might have to revise it a little bit more. I was trying to submit it from work....and last of all, I am new to these challenges so I may need guidance on the input/output structures specific to the challenges. \$\endgroup\$ – Malachi Aug 4 '14 at 22:49
1
\$\begingroup\$

stepAhead Bot: Python 2

Name: stepAhead
Run:  python stepAhead.py [my_hist] [op_hist]

This bot tries to determine the best moves the opponent can make, based on my hands that have lost in the past, and counter them. To reduce the pool of possible counters, hands that have commonly failed in the past are removed, and hands that counter multiple "best moves" or counter commonly used opponent moves are prioritized.

#!/usr/bin/python
# EGYP7
# 08/03/2014
# stepAhead.py: Play to beat the opponent's best move against me.

import sys
from operator import itemgetter

DIV_FACTOR  = 1
HIST_FACTOR = 0.5
DEBUG = True


def beats(hand):
    ''' Return ways to beat "hand".
    '''
    if   hand == 'R':
        return ['P', 'V']
    elif hand == 'P':
        return ['S', 'L']
    elif hand == 'S':
        return ['V', 'R']
    elif hand == 'L':
        return ['R', 'S']
    elif hand == 'V':
        return ['L', 'P']
    else:
        return ValueError


def most_common(in_list):
    ''' Return sorted dictionary of occuring hands and their frequencies.
    '''
    log = [
            ['R', 0],
            ['P', 0],
            ['S', 0],
            ['L', 0],
            ['V', 0]
          ]

    for entry in in_list:
        for sublist in log:
            if entry in sublist:
                sublist[1] += 1

    log = sorted(log, key=itemgetter(1), reverse=True)

    return log


def decide(my_hist, op_hist):
    ''' Reads battle history, analyses and returns best move.
    '''
    global DIV_FACTOR, HIST_FACTOR

    # Find most common losing and winning hands
    wins = []
    loss = []
    for i in range(len(my_hist)):
        if op_hist[i] in beats(my_hist[i]):
            loss.append(my_hist[i])
        elif my_hist[i] in beats(op_hist[i]):
            wins.append(my_hist[i])

    loss_rank = most_common(loss)
    wins_rank = most_common(wins)

    # 1) Find my hands that lose the most
    big_loser = []

    for case in loss_rank:
        # Check for a tie
        if case[1] == loss_rank[0][1]:
            big_loser.append(case[0])
        else:
            break

    counter = []
    for loser in big_loser:
        # Find their likely moves
        for op in beats(loser):
            # Counter those moves
            for count in beats(op):
                counter.append(count)

    # 2) Don't use a counter if it's in the big_loser list
    for count in counter:
        if count in big_loser:
            counter.remove(count)

    # 3) Prioritize reoccuring counters
    counter_num = most_common(counter)

    # 4) Prioritize counters that beat commonly used op_hands
    for candidate in counter_num:
        candidate.append(0)
        for i in range(len(op_hist)):
            if candidate[0] in beats(op_hist[i]):
                candidate[2] += 1

    # 5) Return best counter by weights
    final = sorted(counter_num, key=lambda x: DIV_FACTOR*x[1] + HIST_FACTOR*x[2], reverse=True)

    print final[0][0]
    return


if __name__ == "__main__":
    if len(sys.argv) != 3:
        # First Round: Lizard
        print 'L'
        exit(-1)

    # Not first round
    my_hist = sys.argv[1]
    op_hist = sys.argv[2]

    decide(my_hist, op_hist)

PS: Thank you, Eoin, for opening this question back up to novices like myself!

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

Golfscript - CaptainObvious

Chooses the move which, when played against all of the opponent's previous moves, has the highest score. Golfed down to 73 bytes for fun:

Use this code:

' #'+' '/1=1/:H;"RPSVL"1/.{:x;H{x=},,}%:C;5,{0\{)5%.C=@+~)\}4*;}%.$0=?=

Run with:

echo arg1 arg2 | ruby golfscript.rb CaptainObvious.gs

where golfscript.rb is downloaded from golfscript.com 's "Download" tab

Ungolfed & commented:

' #'+                       #Handles initial case    
' '/1=1/       :History;    #Opponent's move history
"RPSVL"1/      :Options     #The 5 moves, strategically ordered
{
  :x History{x=},,\;
}%
               :Counts;     #Count array corresponding to chars     
5,
{
  )5%. Counts=-1* \
  )5%. Counts=    \
  )5%. Counts=-1* \
  )5%  Counts=
  ]{+}*
}%
.$)\;
?Options=
\$\endgroup\$
1
\$\begingroup\$

The Triangulator: Python 3.2

This program takes the previously played moves, analyses for biases and adds biases to its own moves. Throughout the game it is constantly triangulating its play pattern. (This is my serious entry)

Run:

python triangulator.py [arg1] [arg2]

Code:

import sys, random

# Set all biases to zero for first round
biases = {"R": "-",
          "P": "-",
          "S": "-",
          "L": "-",
          "V": "-"}

beating = {"R": "PV",
           "P": "SL",
           "S": "RV",
           "L": "SR",
           "V": "PL"}

numbertimes = {"R": 0,
               "P": 0,
               "S": 0,
               "L": 0,
               "V": 0}

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

chooser = []

if len(sys.argv) != 3 or len(sys.argv[2]) < 4:
    # Just selects a random choice during first few steps
    print(random.choice(choices))

else:
    oppmoves = sys.argv[2]

    # Finds how many times each move has been played by opponent
    for i in range(0, len(oppmoves)):
        numbertimes[oppmoves[i]] += 1

    # Changes the biases
    for j in range(0, len(choices)):
        if numbertimes[choices[j]]/len(oppmoves) == 1:
            pattern = choices[j]
            moves = beating[choices[j]]
            biases[moves[0]] = "^"
            biases[moves[1]] = "^"

            for k in range(0, len(choices)):
                if not biases[choices[k]] == "^":
                    biases[choices[k]] = "X"

        elif numbertimes[choices[j]]/len(oppmoves) > 0.75:
            pattern = choices[j]
            moves = beating[choices[j]]
            biases[moves[0]] = "!"
            biases[moves[1]] = "!"

        elif numbertimes[choices[j]]/len(oppmoves) > 0.5:
            pattern = choices[j]
            moves = beating[choices[j]]
            biases[moves[0]] = "!"
            biases[moves[1]] = "!"

    # Creates a biased array based upon the previously defined biases
    for a in range(0, len(choices)):
        if biases[choices[a]] == "^":
            it = 16
        elif biases[choices[a]] == "!":
            it = 4
        elif biases[choices[a]] == "-":
            it = 1
        elif biases[choices[a]] == "X":
            it = 0
        for b in range(0, it):
            chooser.insert(len(chooser), choices[a])

    # Prints the move
    print(random.choice(chooser))
\$\endgroup\$
1
\$\begingroup\$

PrediKitty - Perl

BotName: PrediKitty
Compile: Just save as predikitty.pl
Run: perl predikitty.pl [my moves] [opponent moves]

PrediKitty outputs its move to STDOUT as one of RPSLV. The data is inputted on the command line (with PrediKitty's move history first), and it does not matter the history size. (If I've made an error, let me know)
Here's the code:

    # Accept inputs
@mine = split('', $ARGV[0]);
@yours = split('', $ARGV[1]);
# Initialize hashes
%mine_freq = ();
%your_freq = ();
foreach $sym (['R', 'P', 'S', 'L', 'V']) { 
 $mine_freq[$sym] = 0;
 $your_freq[$sym] = 0;
}
# OK, next up, make a possibility pool.
my @possibilities;
$count = 0;
$possibilities[$count][0] = 'R';
$possibilities[$count][1] = 'P';
$possibilities[$count][2] = 'S';
$possibilities[$count][3] = 'L';
$possibilities[$count][4] = 'V';
$count++;
# Now gather data
foreach $sym (@mine) {
 $mine_freq[$sym] = $mine_freq[$sym] + 1;
}
# Here's where it gets fun: Based on earlier predictions, add to the pool!
foreach $sym (@yours) {
 $your_freq[$sym] = $your_freq[$sym] + 1;
 # Add two of each of the things that beat it, and one that ties it (in case we are wrong)
 if ($sym == 'R') {
$possibilities[$count][0] = 'R';
$possibilities[$count][1] = 'P';
$possibilities[$count][2] = 'P';
$possibilities[$count][3] = 'V';
$possibilities[$count][4] = 'V';
 } elsif ($sym == 'S') {
$possibilities[$count][0] = 'S';
$possibilities[$count][1] = 'R';
$possibilities[$count][2] = 'R';
$possibilities[$count][3] = 'V';
$possibilities[$count][4] = 'V';
 } elsif ($sym == 'P') {
$possibilities[$count][0] = 'P';
$possibilities[$count][1] = 'S';
$possibilities[$count][2] = 'S';
$possibilities[$count][3] = 'L';
$possibilities[$count][4] = 'L';
 } elsif ($sym == 'L') {
$possibilities[$count][0] = 'L';
$possibilities[$count][1] = 'S';
$possibilities[$count][2] = 'S';
$possibilities[$count][3] = 'R';
$possibilities[$count][4] = 'R';
 } elsif ($sym == 'V') {
$possibilities[$count][0] = 'V';
$possibilities[$count][1] = 'P';
$possibilities[$count][2] = 'P';
$possibilities[$count][3] = 'L';
$possibilities[$count][4] = 'L';
 } else {
  next; # If somewhere a bot fails, this shouldn't affect us
 }
  $count++;
}
# Now, get a random pool from the meta-pool (which is my new invention)
THROW: {
$pool_num =  int(rand(( scalar @possibilities )));
# And a choice
$choice_num = int(rand(5));
# This is to add some extra random - You never know when you'll need it...
$pick_random = int(rand(50));
if ($pick_random == 0) {
 $pool_num = 0; # Go back to our random pool
}
$redo = int(rand(10));
if ($redo == 0) {
 redo THROW;
}
# Now calculate and output!
$chosen_throw = $possibilities[$pool_num][$choice_num];
print $chosen_throw;
}
# Hey, did you really scroll all the way down here? Good job!
# Have a smiley. ;D

I will be happy to update should the terms of the challenge change. But for now, this should rock! Or should I say rock-paper-scissors-lizard-spock? No, I shouldn't.

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

UnlimitedMarkovBot (java)

Try to find how the previous steps affected the opponent play and adapt

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

        char[] plays = new char[]{'R','S','L','P','V'};

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

        char[] myHis = args[0].toCharArray();
        char[] opHis = args[1].toCharArray();

        int[] myHis_n = new int[myHis.length];
        int[] opHis_n = new int[opHis.length];

        for (int i = 0; i < myHis.length; i++){
            for (int k = 0; k < 5; k++){
                if (myHis[i] == plays[k])
                    myHis_n[i] = k;
                if (opHis[i] == plays[k])
                    opHis_n[i] = k;
            }
        }


        double [][][] myMarkovMatrix = new double[myHis.length][5][5];
        double [][][] opMarkovMatrix = new double[myHis.length][5][5];

        double[] estProb = new double[5];

        for (int i = 0; i < myHis.length; i++){
            for (int j = 0; j < i; j++){
                    myMarkovMatrix[i-j][myHis_n[j]][opHis_n[i]] +=1;
                    opMarkovMatrix[i-j][opHis_n[j]][opHis_n[i]] +=1;
            }
        }

        double coeff = 0.8;
        double c = 1;
        for (int i = 1; i < myHis.length; i++){
            for (int k = 0; k < 5; k++){

                estProb[k] += c*myMarkovMatrix[i][myHis_n[myHis.length - 1]][k];
                estProb[k] += c*opMarkovMatrix[i][opHis_n[myHis.length - i]][k];
                c*= coeff;
            }
        }
        int best = 0;
        double bestScore = 0;
        for (int k = 0; k < 5; k++){
            if ( estProb[k]+2*estProb[(k+1)%5]+ 2*estProb[(k+2)%5]> bestScore){
                bestScore = estProb[k]+2*estProb[(k+1)%5]+ 2*estProb[(k+2)%5];
                best = k;

            }
        }
        System.out.print(plays[best]);
    }
}
\$\endgroup\$
0
\$\begingroup\$

PatternFinder

BotName: PatternFinder
Run: python3 patternFinder.py [mine] [his]

It's based on the code that's giving me the best results in the Loaded RPS, where it looks for pattern of an opponent taking into account my last play or his (and stick to the one he seems to be using).

import sys
import random
import numpy as np

if __name__ == "__main__":

        if len(sys.argv) != 3:
                print("R")
                exit(-1)

        my_history = sys.argv[1]
        opp_history = sys.argv[2]

        t = len(opp_history)
        RPS = ["R","P","S","L","V"]
        if t < 5:
                print(RPS[t])
        elif t == 5:
                print(random.choice(RPS))

        def n(c): return RPS.index(c)

        total_me = np.zeros(shape=(5,5))
        total_opp= np.zeros(shape=(5,5))
        p_me = np.array([[.2]*5]*5)
        p_opp = np.array([[.2]*5]*5)

        for i in range(1, t):
                total_me[n(my_history[i-1]), n(opp_history[i])] += 1
                total_opp[n(opp_history[i-1]), n(opp_history[i])] += 1
        for i in range(3):
                if np.sum(total_me[i,:]) != 0:
                        p_me[i,:] = total_me[i,:] / np.sum(total_me[i,:])
                if np.sum(total_opp[i,:]) != 0:
                        p_opp[i,:] = total_opp[i,:] / np.sum(total_opp[i,:])

        error_me = 0
        error_opp = 0

        for i in range(1, t):
                diff = 1 - p_me[n(my_history[i-1]), n(opp_history[i])]
                error_me += diff * diff
                diff = 1 - p_opp[n(opp_history[i-1]), n(opp_history[i])]
                error_opp += diff * diff

        if error_me < error_opp:
                p = p_me[n(my_history[-1]),:]
        else:
                p = p_opp[n(opp_history[-1]),:]

        win = [[2,3],[0,4],[1,3],[1,4],[0,2]]
        lose = [[1,4],[2,3],[0,4],[0,2],[1,3]]

        best_score = -1000
        for i in range(5):
                E = p[win[i][0]] + p[win[i][1]] - p[lose[i][0]] - p[lose[i][0]]
                if E > best_score:
                        best_idx = i

        print(RPS[best_idx])
\$\endgroup\$

Your Answer

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

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