The Rock, Paper, Scissors, Lizard, Spock Tournament of Epicness

98

42

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.

Eoin Campbell

Posted 2014-07-25T15:39:06.563

Reputation: 1 101

I see everyone else has a small description of how theirs works, is this necessary or optional? – ASCIIThenANSI – 2015-04-06T19:16:34.387

1Also, how often do you add new bots? I made my bot, and I was curious to how long it takes to get added. – ASCIIThenANSI – 2015-04-06T19:46:59.570

7What does the history look like when a player has forfeited a hand? – histocrat – 2014-07-25T17:41:25.210

1I was going to go all-out with an analytic approach, but most of the bots here are stupid enough to defeat smart AI. – fluffy – 2014-07-25T21:23:34.280

@histocrat - good question, I hadn't considered a 6th character to represent an error/forfeited hand. let me think about this and I'll update tomorrow. – Eoin Campbell – 2014-07-25T21:27:31.600

1Just 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 – 2014-07-26T02:55:08.760

1pics or it didn't happen. amirite – Eoin Campbell – 2014-07-26T08:16:44.690

*NB* 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. – Eoin Campbell – 2014-07-26T11:48:45.517

Deleted Deadpool due to this slight rule change. – histocrat – 2014-07-26T13:36:14.677

Can't we just use the bots from http://www.rpscontest.com/ and modify them to 5 instead of 3 outcomes? I think at least this site should be mentioned! And please let the participants explain the ideas of their algorithms!

– flawr – 2014-07-26T14:21:29.567

Can we modify the bot once we post it, or do we have to post a new one? And how do we let you know it has been modified? – Claudiu – 2014-07-26T18:03:51.933

@flawr. sure I don't see why not. and participants are free to explain their bots. in fact I'd recommend it. Most of the results I'm seeing from bots which run on pure PRNG decisions are get v. mixed results anyway. – Eoin Campbell – 2014-07-26T19:34:44.907

@claudiu... if you want to refine your bot, that's ok. HuddleWolf has done this already. just post a comment here at the top so I don't miss the mod. – Eoin Campbell – 2014-07-26T19:35:37.700

The lack of forfeits should probably be mentioned in the question itself, not just here in the comments. Right now, the question as stated not only allows for them but specifically mentions how they are handled. – trlkly – 2014-07-27T11:03:24.617

@trlkly agreed & noted – Eoin Campbell – 2014-07-27T19:17:20.703

*NB* the control program is now posted on github. needs a bit of work to parallelize the matches due to the number of games now being played. – Eoin Campbell – 2014-07-27T21:56:37.510

2014 07 27 Tourney Result are updated... MarkovBot gets the 'W' – Eoin Campbell – 2014-07-28T01:21:14.913

@EoinCampbell are the raw results available somewhere? I want to know who beat my bot. :) – Emil – 2014-07-28T07:26:06.167

3I'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 – 2014-07-28T07:55:18.707

@EoinCampbell just to let you know, I made a small change to my code (Pony). Would be great if you could include that in one of the next runs. Thanks. – Emil – 2014-07-28T20:58:50.120

Just in the nick of time... just about to kick off a run in 5 :-) – Eoin Campbell – 2014-07-28T21:26:55.940

Ok. Next tourney just starting. 38 Players. 703 Games. 140,600 hands. Est. 90 minute run time. Lots of statistics and log outputs coming as well – Eoin Campbell – 2014-07-28T21:44:14.277

3If 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 – 2014-07-29T07:07:49.833

1you could be very well be correct... I was hacking away at it the last 2 evenings to try and improve the processing speed since I can't really parallelize things. Quite possible I messed it up while refactoring the ARGS builder to support java/executable/scripts. – Eoin Campbell – 2014-07-29T07:31:39.243

@Emil very well spotted dude. looks like I introduced that bug on Sunday night before I committed the code to github while doing some refactoring. I'll fix tonight and do another run. – Eoin Campbell – 2014-07-29T07:34:17.013

That's definitely the case. Markov (btw, there was a never a Bot in the name :P) should be playing perfectly against Vulcan but isn't. Also in general it tends to lose or play very close matches if it's the second player, and wins almost all matches (some of them perfectly) if it's the first player. – Martin Ender – 2014-07-29T07:34:37.373

Feels like a dope sorry guys... note taken to fix it. My bad :-( https://github.com/eoincampbell/big-bang-game/commit/fc8a1548f435ec5d36f548d61078c9582b7f3725#commitcomment-7183979 – Eoin Campbell – 2014-07-29T07:35:50.297

LOOKING FOR FEEDBACK - Should I consider setting a final submission date to cap the number of players. The last tournment involved 38 Players (703 matches) and took 90 minutes to run. Extrapolating. 50 players ~= 1250 games ~= 2.5 hrs. 60 players ~= 1800 games ~= 4 hrs. 70 players ~= 2500 games ~5.5hrs. - Only down side of more players is I might have to start running over night tourneys so I'd update every 2nd or 3rd day – Eoin Campbell – 2014-07-29T08:13:57.097

@EoinCampbell I'd cap it at the end of the week, or restrict each person to only one bot. – Οurous – 2014-07-29T08:32:58.760

@Ourous noted :-) – Eoin Campbell – 2014-07-29T10:37:35.457

@EoinCampbell If you don't mind running them over night every other day, I'd prefer not setting a submission deadline. You will probably get fewer and fewer submissions anyway at some point, so that running it once a week should then be enough. And if you don't get another submission for a week you can still decide to close it. I think the Wolf KotH is technically still open today, just that no one has posted a submission in a while. – Martin Ender – 2014-07-29T11:44:28.230

@MartinBüttner - ok. won't lock it down for now. thanks guys. – Eoin Campbell – 2014-07-29T12:50:53.323

@EoinCampbell Are you regularly updating submissions? I notice that my bot is still labeled as HuddleWolfHatesBigBangTheory and since changing the name to HuddleWolfTheConqueror, I've made a handful of logic improvements. – HuddleWolf – 2014-07-29T12:51:11.753

I've been updating code. but didn't update the bot names. I'm going to clean these up in the next run as I ended up calling a number of them NameBot where it was just called Name – Eoin Campbell – 2014-07-29T13:18:12.570

1CasinoShakespeare didn't managed into your tournament without any info. – Daniel Kmak – 2014-07-29T16:23:49.593

1hmmm... missed that one. Sorry @Daniel will look at it now. – Eoin Campbell – 2014-07-29T17:00:04.693

Same with IocainePowder, it wasn't included... just checking, since I'd posted it yesterday before the new results were posted. – jmite – 2014-07-29T17:54:57.663

New run coming with a bunch of new bots incl. you guys. – Eoin Campbell – 2014-07-29T18:11:57.563

1Okay, just checking. Thanks for the great work! – jmite – 2014-07-29T18:14:50.820

1just kicked it off... in a 1 hand per game dry run, it took 115 seconds for 1,326 games incl. 52 players. So guessing this run will take 3.5 hours. will update again later tonight (if my wife hasn't divorced me at that stage) – Eoin Campbell – 2014-07-29T18:52:51.297

Damn! I thought mine was good! I was in fifth place last time. Oh well. – Milo – 2014-07-30T04:52:51.880

@EoinCampbell I submitted a PR with parallelisation. Hope it helps. – Andrew Vermie – 2014-07-30T07:57:05.833

@AndrewVermie yep. got it man. really appreciate it. you cracked that data access concurrency problem that I was avoiding. I'll do some 3-hand dry runs tonight to test it and and if all is good, I'll run the next tournament using it. quad core laptop so should help. Thanks again. – Eoin Campbell – 2014-07-30T08:20:26.270

I never liked Scissors Paper Stone game, as it's impossible to be better than randomly pick any move, if we're fighting an opponent taking random moves. The best method, then, depends on the number of people submitting non-random move. =D – justhalf – 2014-07-30T09:32:02.113

which there are now a significant number of. top 5 are all good algos. Winner was a submission used for a previous RPS game. In any sample space, the guys picking pure randoms should be mid table, and the guys spamming should be at the bottom. which would seem to be the case. – Eoin Campbell – 2014-07-30T11:14:28.947

updated rambo_bot – john Smith – 2014-07-30T23:30:04.733

when will be the next tournament ? – john Smith – 2014-07-31T09:09:37.480

It's running at home at the minute. Will post results tonight. Just FYI, I've updated the log file mechanism, I'll be posting the full touney log as before, but now, each player gets their own dump file of all matches they participated in so that they can see results. – Eoin Campbell – 2014-07-31T09:48:25.337

New Tournament results are up now... Congrats to @Emil on another win. the linked zip file contains per-player logfiles now so it should be easier to find out how your bot performed. Next tournament will probably be Sunday Night, results monday morning. ~E – Eoin Campbell – 2014-08-01T07:37:11.463

you forgot to add rambo_bot :( – john Smith – 2014-08-01T08:50:51.377

Crap! sorry man. not sure how that happened. cos I wasn't +1'ing answers until I had added them to the control prog. will make sure it's in the next run. I'll kick off another run tonight so you don't have to wait til Monday. – Eoin Campbell – 2014-08-01T08:56:17.433

I can't tell, are you still updating code for each new tournament if we edit our program? I wouldn't blame you if you didn't (that's a lot of work) but I'm just curious if my edits are accomplishing anything. – Qwix – 2014-08-01T13:46:42.640

1

yes. I track the answer list below by "active" and scroll back through submissions to update code before each run. if you're worried I've missed an update, check out https://github.com/eoincampbell/big-bang-game/tree/synchronous/BigBang.Orchestrator/Players/Analyst to see if Iv'e got your latest code.

– Eoin Campbell – 2014-08-01T13:52:43.527

I'd like to ask why all this kind of "king of the hill game" use argument passing as input? If we use stdin instead, then we don't need to start the interpreters(which will cost about 0.1s) every round and that would be a lot faster. One may finish a size 70 tournament in several minutes. I'm not asking to change the rule of this game. I just want to know why. – Ray – 2014-08-01T15:29:47.567

shrugs from my POV I've writing the controller prog in .net. All processes, be they exe's or script languages are exec'd from a System.Diagnostic Process... it's just handy to pass the params as args. That's just how I happened to bang the controller program together though. – Eoin Campbell – 2014-08-01T16:16:26.053

1

@Ray There have been KotHs and code challenges before which have used persistent pipes instead, e.g. this, this and this. Often it just depends on what the OP finds easier or more practical. The appeal of ARGV is also that it's a bit easier for players to write their bots if they don't need to include some form of communication loop.

– Martin Ender – 2014-08-01T23:53:25.230

@MartinBüttner Thanks for the examples. Yes it can help the players strip a loop, but it cost us much more time. – Ray – 2014-08-02T02:10:03.000

new tourney results are up – Eoin Campbell – 2014-08-02T11:56:39.753

Answers

26

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.

Emil

Posted 2014-07-25T15:39:06.563

Reputation: 1 438

3A 96 percent win ratio is outstanding. – AndoDaan – 2014-07-31T00:56:30.623

Very nice. You may like Iocaine Powder, if you haven't seen it already.

– wchargin – 2014-07-31T05:14:48.283

@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 – 2014-07-31T06:00:18.163

20

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

Martin Ender

Posted 2014-07-25T15:39:06.563

Reputation: 184 808

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 – 2014-07-29T00:35:34.800

@Jamie You mean like this guy? http://codegolf.stackexchange.com/a/35295/8478

– Martin Ender – 2014-07-29T06:51:58.890

you guess it. (the comment was not long enough to be posted) – Jamie – 2014-07-29T23:22:58.923

19

ConservativeBot, Ruby

New things are bad things.

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

Run like

ruby conservative.rb

Martin Ender

Posted 2014-07-25T15:39:06.563

Reputation: 184 808

OG version is the best version. – maxywb – 2014-07-25T16:25:37.870

13

Star Wars Fan - Ruby

Screw you, Spock

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

Run it like:

ruby starwarsfan.rb

William Barbosa

Posted 2014-07-25T15:39:06.563

Reputation: 3 269

Added to Controller – Eoin Campbell – 2014-07-25T17:50:25.443

you can rollback by answer edit - I'll just comment here when I've added them. – Eoin Campbell – 2014-07-25T17:50:43.483

Why R and S? :P – cjfaure – 2014-07-26T13:48:00.917

@mardavi It is a Star Wars fan because it does not use Spock. – William Barbosa – 2014-07-28T10:47:34.740

ah, you are right (of course). I read it too fast, my mistake (but without consequences luckily) – mardavi – 2014-07-28T11:05:00.847

13

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

William Barbosa

Posted 2014-07-25T15:39:06.563

Reputation: 3 269

8

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];
    }
}

HuddleWolf

Posted 2014-07-25T15:39:06.563

Reputation: 223

8

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;
        }
    }
}

kaine

Posted 2014-07-25T15:39:06.563

Reputation: 536

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 – 2014-07-25T18:15:13.127

is this C#. you're .length properties are wrong. and theres no method max – Eoin Campbell – 2014-07-25T18:46:50.657

@EoinCampbell It is java, i've been playing with both and apparently forgot which commands belong to which. – kaine – 2014-07-25T19:09:16.147

ah cool. leave it with me and I'll get it included. – Eoin Campbell – 2014-07-25T19:12:03.647

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.java - – Eoin Campbell – 2014-07-26T00:24:10.053

Main shoul have been main – kaine – 2014-07-26T13:18:47.677

yeah I figured that out... cheers – Eoin Campbell – 2014-07-26T17:56:21.617

Good ol' rock, nothing beats it! – Sammitch – 2014-07-28T21:09:54.810

8

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;
    }
}

Stranjyr

Posted 2014-07-25T15:39:06.563

Reputation: 111

1Should we be using print or println?... I wasn't certain. – kaine – 2014-07-25T20:31:06.537

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 – 2014-07-25T20:34:34.240

@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 – 2014-07-30T13:09:52.960

@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 – 2014-07-30T13:32:58.653

Cool. Have it updated in the control prog now. – Eoin Campbell – 2014-07-31T17:09:36.653

8

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

Dr R Dizzle

Posted 2014-07-25T15:39:06.563

Reputation: 213

2Minor name clash. +1 anyway. – Martin Ender – 2014-07-28T14:06:26.067

@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 – 2014-07-28T14:17:08.550

1Sheldor agrees... there shall be a BartBot and a BartSimpson :) – Eoin Campbell – 2014-07-28T15:36:02.763

3We only have BortBot. – JoshDM – 2014-07-28T18:42:49.487

1These will get slaughtered by markov :) – Cruncher – 2014-07-29T19:03:38.430

8

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

jmite

Posted 2014-07-25T15:39:06.563

Reputation: 271

5You keep using that word. I do not think it means what you think it means. – JoshDM – 2014-07-28T20:25:37.490

2The 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 – 2014-07-30T14:39:32.160

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 – 2014-07-30T17:11:12.903

7

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]);
            }
        }
    }
}

ProgramFOX

Posted 2014-07-25T15:39:06.563

Reputation: 8 017

7

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

Martin Ender

Posted 2014-07-25T15:39:06.563

Reputation: 184 808

Need to look back at the episodes to see if anyone was born with a forked tongue. LizardMan FTW !!! – Eoin Campbell – 2014-07-25T17:05:27.160

3But isn't this how everyone on big bang plays anyways? – kaine – 2014-07-25T17:24:14.880

2@anotherguest That's what I meant by "this is the only in-character strategy". – Martin Ender – 2014-07-25T17:24:55.810

6

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");     
    }
}

Mikey Mouse

Posted 2014-07-25T15:39:06.563

Reputation: 301

6

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

PhiNotPi

Posted 2014-07-25T15:39:06.563

Reputation: 26 739

5

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

Tymric

Posted 2014-07-25T15:39:06.563

Reputation: 671

Yeah, thought about that. – seequ – 2014-07-28T08:15:52.597

5

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");


?>

ArcticanAudio

Posted 2014-07-25T15:39:06.563

Reputation: 111

5

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.

Stretch Maniac

Posted 2014-07-25T15:39:06.563

Reputation: 3 971

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) – Justin – 2014-07-26T04:49:31.920

Thanks! As a relatively new programmer I was not aware of this. – Stretch Maniac – 2014-07-26T13:39:08.150

4

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

Martin Ender

Posted 2014-07-25T15:39:06.563

Reputation: 184 808

4

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

Kyle Kanos

Posted 2014-07-25T15:39:06.563

Reputation: 4 270

@MartinBüttner: Command added! I have been pretty busy at work trying to get something published, hence the disappearance. – Kyle Kanos – 2014-07-25T16:31:57.673

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 – 2014-07-25T19:30:10.757

@EoinCampbell: I forgot the exit clause on first run, it's been added & should work fine now. – Kyle Kanos – 2014-07-25T19:55:20.260

4

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.

histocrat

Posted 2014-07-25T15:39:06.563

Reputation: 20 600

4

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

undergroundmonorail

Posted 2014-07-25T15:39:06.563

Reputation: 5 897

On what condition does TheGambler change MODE? – Dr R Dizzle – 2014-07-29T14:50:00.567

@DrRDizzle It doesn't, it looks like this is a submit of two bots in one. – Paŭlo Ebermann – 2014-07-29T16:17:30.990

2Would 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 – 2014-07-31T09:00:24.920

4

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.

histocrat

Posted 2014-07-25T15:39:06.563

Reputation: 20 600

4

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;
    }
}

JoshDM

Posted 2014-07-25T15:39:06.563

Reputation: 171

4

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;
        }
    }
}

Qwix

Posted 2014-07-25T15:39:06.563

Reputation: 131

4

Dienstag (Python 2)

My first entry Pony seems to fare quite well with all its second guessing (tripple guessing, ...) and meta reasoning. But is that even necessary?

So, here is Dienstag, Pony's little friend, with only one of the 55 strategies: Predict the opponent's next move and beat it.

On the long run Dienstag wins or ties with every Bot in the top ten of the current leaderboard. Except for Pony that is.

import sys
if len(sys.argv)<2 or len(sys.argv[1])<2: print 'L'; sys.exit()
hist = [map('RPSVL'.index, p) for p in zip(sys.argv[1], sys.argv[2])]
N = len(hist)
cand = range(N-1)
for l in xrange(1,N):
    cand = ([c for c in cand if c>=l and hist[c-l+1]==hist[-l]] or cand[-1:])
print 'RPSVL'[(hist[cand[-1]+1][1]+(1,3)[N%2==0])%5]

Run as:

python Dienstag.py

I admit that the code is a bit obfuscated. Should anyone care to know more about it, I might add explanations.

Edit: Here is a short example walkthrough to explain the idea:

  • The program gets the history of it's own and the opponent's moves:

    sys.arg[1] = 'LLVLLVL', sys.arg[2] = 'RPSPSSP'

  • The history is combined into a list of pairs and the moves are translated to numbers (R=0, ...):

    hist = [[4, 0], [4, 1], [3, 2], [4, 1], [4, 2], [3, 2], [4, 1]]

  • The number of rounds played so far is determined:

    N = 7

  • The basic idea now is to look for the longest unbroken chain of exactly the last moves in the earlier history. The program keeps track of where such a chain ends in the list cand (for 'candidates'). In the beginning, without checking, every position in the history except for the last one is considered:

    cand = [0, 1, 2, 3, 4, 5]

  • Now the length of possible chains is increased step by step. For chain length l = 1 it looks for previous occurrences of the last move pair [4, 1]. This can be found at history position 1 and 3. Only these are kept in the cand list:

    cand = [1, 3]

  • Next, for l = 2 it checks which of the possible candidates was preceded by the second to last move pair [3, 2]. This is only the case for position 3:

    cand = [3]

  • For l = 3 and more there are no previous chains of that length and cand would be empty. In this case the last element of cand is kept:

    cand = [3]

  • The bot now assumes that history will repeat. The last time the cain [3, 2], [4, 1] occurred, it was followed by [4, 2]. So, the opponent played 2 (scissors) which can be beaten by (2+1)%5 = 3 (Spock) or (2+3)%5 = 0 (rock). The bot answers, with the first or second alternative depending on whether N is odd or even just to introduce some variance.

  • Here move 3 is chosen which is then translated back:

    print 'V'

Note: Dienstag has time complexity O(N2) for returning the next move after N rounds. Pony has time complexity O(N3). So in this aspect they are probably much worse then most other entries.

Emil

Posted 2014-07-25T15:39:06.563

Reputation: 1 438

please do. this is an awesome learning experience for me. I usually live in C#/Java land so all the lua,ruby,python,haskell madness is very interesting to me. – Eoin Campbell – 2014-08-01T10:07:26.747

I'm also tempted to add an extra instance of Pony to the games. It'll be like having to fight your mirror self in the 3rd to last Mortal Combat levels ;-) – Eoin Campbell – 2014-08-01T10:27:35.313

@EoinCampbell :-) At least the direct match Pony vs. Pony would be a perfect draw. There is no element of randomness in both my bots. – Emil – 2014-08-01T11:30:29.703

3

FairBot, Ruby

Let's start simple.

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

Run like

ruby fairbot.rb

Martin Ender

Posted 2014-07-25T15:39:06.563

Reputation: 184 808

small typo on that last 'V' param. have fixed it on myside if you wanna update for completeness – Eoin Campbell – 2014-07-25T18:50:05.553

@EoinCampbell thanks, fixed! – Martin Ender – 2014-07-25T18:56:07.980

1The interesting thing is that this has exactly equal odds of winning against ALL strategies. – Cruncher – 2014-07-29T18:58:06.387

3

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)

Kyle Kanos

Posted 2014-07-25T15:39:06.563

Reputation: 4 270

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 – 2014-07-25T19:26:43.690

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 – 2014-07-25T19:27:10.573

@EoinCampbell: I've added an exit clause for the first run, you should be able to run it now. – Kyle Kanos – 2014-07-25T19:55:59.577

3

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

mccannf

Posted 2014-07-25T15:39:06.563

Reputation: 151

5After reading the title, I'm slightly disappointed that you do anything but R. ;) – Martin Ender – 2014-07-25T16:53:49.817

@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 – 2014-07-27T20:44:57.050

@EoinCampbell - when you create the file in windows, can you then run dos2unix on the file in cygwin before executing it? – mccannf – 2014-07-27T21:00:34.750

sure. I'll give that a try. – Eoin Campbell – 2014-07-27T21:13:03.110

I think the problem might be with the /dev/urandom statement – Eoin Campbell – 2014-07-27T21:20:59.883

Works for me in Cygwin with UNIX file as above and with C:\Cygwin\bin added to PATH - but have seen those types of errors before due to crlf issues in files. – mccannf – 2014-07-27T21:25:29.343

@EoinCampbell - the sh script here (gunzipped) works for me on cygwin with C:\Cygwin\bin added to the PATH, and running bash.exe bashrocks.sh.

– mccannf – 2014-07-28T14:31:08.563

mccannf. not having much joy with this. When I run it from a regular cmd prompt, with all the cygwin exes in my %PATH% it works perfectly, but when I exec it from inside a .NET diagnostics.process, it breaks. And when I explicity replace the commands with C:/cygwin/bin/command, I get parsing errors. Have a couple of ideas to fix it. will keep you posted, or if you want you can ping me on skype. skype handle is my fullname, no spaces. – Eoin Campbell – 2014-07-29T07:46:00.870

3

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

Vectorized

Posted 2014-07-25T15:39:06.563

Reputation: 3 486

1Summary 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 – 2014-07-26T04:23:56.093

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. ;) – Vectorized – 2014-07-26T06:35:43.107

3

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'

DrJPepper

Posted 2014-07-25T15:39:06.563

Reputation: 499

3

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

Claudiu

Posted 2014-07-25T15:39:06.563

Reputation: 3 870

3

LoopholeAbuser

I'm withdrawing this bot from the tourney now that the rules have been patched--without the sneakiness, it's basically a slightly worse version of Bart.

@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
  when 0
    'P'
  when 1
    'R'*100
  when 2
    'L'
  when 3
    'S'
  when 4
    'V'
  else
    extrapolate(player, opponent)
  end

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(:+)
  end
end

puts move

Just doing these because for once I want to get to the loopholes before other people. If you consider this to be legal (it doesn't throw an error or cause an opponent to throw an error, but it does forfeit a round), run with ruby loophole_abuser.rb.

histocrat

Posted 2014-07-25T15:39:06.563

Reputation: 20 600

:-) haha. nice try. I'll change the controller to validate that it takes only the first character of the first line of STDOUT. Damn you're keeping me on my toes. I gotta refine my KOTH definitions for the next game. Or bring you for a beer first :-) – Eoin Campbell – 2014-07-28T01:27:10.227

What does (or did) this do? – justhalf – 2014-07-30T09:34:19.067

1It plays a simple Markov-y strategy, except on the second round it plays 100 rocks at once. This was a forfeit, but it showed up in the history fed to the opponent as a hundred separate moves, making me look like BartSimpson. – histocrat – 2014-07-30T13:19:50.607

cool... noted & withdrawn – Eoin Campbell – 2014-07-31T17:05:20.573

3

CounterPreference bot PHP

First response on this site, lets hope I get it right.

First round it responds randomly. From there it analyses the opponents hand and plans a counter, but if the opponents has a history of predicting this correctly, it will try to avoid falling for that trap again.

Thanks Martin for pointing out my oversight. I have to admit I do not have any experience with command-line and php. But if I understand it correctly it should work as this. Il test it out when I am on a machine I can access the command line with.

Well, this is turning out to be quite educative. Sadly still not able to test. So I apologize if it still does not work.

<?php

$input = $argv;

counterPreferenceBot($input[1], $input[2]);

function counterPreferenceBot($myHandHistory = null, $opponentHandHistory = null)
{


//No Arugments supplied
    if($myHandHistory == null)
    {
        //Random!
        $choices = array("R", "P", "S", "L", "V");
        echo $choices[rand(0, 4)];
    }
    else
    {
        //Arguments supplied
        //Make list of avaiable options
        $r = 0;
        $p = 0;
        $s = 0;
        $l = 0;
        $v = 0;
        foreach($opponentHandHistory as $round => $oppentChoice)
        {
            //Find opponents weaknesses on which it used most.
            if($oppentChoice == "R"){$p++; $v++;}
            else if($oppentChoice == "P"){$s++; $l++;}
            else if($oppentChoice == "S"){$v++; $r++;}
            else if($oppentChoice == "L"){$s++; $r++;}
            else if($oppentChoice == "V"){$l++; $p++;}

            //But avoid falling into whatever clever trap the enemy uses.
            if($myHandHistory[$round] == "R" AND ($oppentChoice == "P" OR $oppentChoice == "V"))
            {   $r--;       
            }else if($myHandHistory[$round] == "P" AND ($oppentChoice == "S" OR $oppentChoice == "L"))
            {   $p--;
            }else if($myHandHistory[$round] == "S" AND ($oppentChoice == "V" OR $oppentChoice == "R"))
            {   $s--;
            }else if($myHandHistory[$round] == "L" AND ($oppentChoice == "S" OR $oppentChoice == "R"))
            {   $l--;
            }else if($myHandHistory[$round] == "V" AND ($oppentChoice == "L" OR $oppentChoice == "P"))
            {   $v--;
            }
        }

        $result = array("R" => $r, "P" => $p, "S" => $s, "L" => $l, "V" => $v);
        //Handsomely stolen code from Micheal Angel to sort those 5 ints into the highest for the right choice.
        $maxvalue = max($result);
        while(list($key,$value)=each($result)){
            if($value==$maxvalue)$maxindex=$key;
        }
        $choice = array("m"=>$maxvalue,"i"=>$maxindex);

        echo $choice['i'];  
    }   
}

?>

Hikata Ikaruga

Posted 2014-07-25T15:39:06.563

Reputation: 31

1Welcome to PPCG.SE! For this challenge, you'll need to provide a complete script that can be run from the command line. You might want to append some code to your snippet which reads the command-line arguments and passes them to your function. Then you should also add the corresponding command to run your bot (probably php counterpreferencebot.php). – Martin Ender – 2014-07-28T11:27:50.557

@MartinButtner Thanks – Eoin Campbell – 2014-07-28T18:32:57.900

1Hey Hikata. Welcome to CodeGolf. I've tried run your script but you'll need to modify it to read from ARGS instead of STDIN. Thanks – Eoin Campbell – 2014-07-28T18:33:38.497

3

Uniform (Ruby)

BotName: Uniform
Run:     ruby uniform.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("")

  c = @counts.clone

  if( mine.length < 50 )
    if( mine.length.even? )
      mine.values_at(* mine.each_index.select {|i| i.even?}).each {|x| c[x]+=1}
      @counts.keys.each {|x| if( c[x]>4 ) then c.delete(x) end}
    else
      mine[0..-1].each_index.select {|i| i.even?}.each {|x| if( mine[x]==mine[-1] ) then c.delete(mine[x+1]) end}
    end
    choices = c.keys
  else
    c2 = @counts.clone
    for i in 0 .. mine.length - 3
      if( mine[i..i+1].join == mine[-2..-1].join ) then c[theirs[i+2]]+=1 end
    end
    bestCount = 0
    @counters.keys.each {|x| @counters[x].each {|y| c2[y]+=c[x];c2[x]+=c[x]*0.4}}
    @counters.keys.each {|x| if( c2[x] > bestCount ) then bestCount = c2[x] end}
    @counters.keys.each {|x| if( c2[x] == bestCount ) then choices.push( x ) end}
  end
else
  choices = @counters.keys
end

puts choices.sample

Thaylon

Posted 2014-07-25T15:39:06.563

Reputation: 1 324

bugs sometimes for this.

D:/My Software Dev/big-bang-game/BigBang.Orchestrator/bin/Debug/Players/UniformBot/UniformBot.rb:23:in block in <main>': undefined method+' for nil:NilClass (NoMethodError) from D:/My Software Dev/big-bang-game/BigBang.Orchestrator/bin/Debug/Players/UniformBot/UniformBot.rb:22:in each' from D:/My Software Dev/big-bang-game/BigBang.Orchestrator/bin/Debug/Players/UniformBot/UniformBot.rb:22:in<main>' – Eoin Campbell – 2014-07-28T18:23:48.893

@EoinCampbell Sorry, should be fixed now ... hopefully ... – Thaylon – 2014-07-28T18:53:45.080

3

LemmingBot

Yep, this is a lemming and his only goal is to die (lose each game). But because he isn't that smart, he always hopes for the other player to make the last move again.

import sys, random

def totallyRandomStarterPick() :
    return "P"    ### Chosen by fair dice roll

picks = { "S" : "PL", "P" : "RV", "R" : "LS", "L" : "VP", "V" : "SR" }
confused = lambda p : random.choice(picks[p])

try :
    opn = sys.argv[-1][-1]
    print(confused(opn))    ### Kill me!
except Exception :
    print(totallyRandomStarterPick())

Written in Python 2.7, run with python LemmingBot.py [args1] [args2] (args1 = own hands, args2 = opponents hands).

Please help him, he really wants to die :(

EDIT: LemmingBot goes mad. Added a bit of randomness for confusing his opponents (and himself).

Cipher

Posted 2014-07-25T15:39:06.563

Reputation: 1 321

2So, I checked the logs, and your suicidal Lemmingbot beat my toddleProof bot 100 - 0 because our patterns exactly coincided. Ouch.... – Stranjyr – 2014-07-29T14:36:10.133

That's Brilliant :-) – Eoin Campbell – 2014-07-30T12:37:59.287

3

Java - SelfHatingBot

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

Bot starts randomly, then always plays a winning move against its own prior move with 50% choice of tactic.

import java.util.Random;

public class SelfHatingBot {

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

        char play;

        if (args.length == 0) {

            play = randomPlay();

        } else {

            play = antiPlay(args[0]);
        }

        System.out.print(play);
        return;
    }
}

JoshDM

Posted 2014-07-25T15:39:06.563

Reputation: 171

3

Edward Scissorhands

No one had made a Scissor spam bot, so I did.

puts 'S'

Run as

ruby EdwardScissorhands.rb

Daenerys Targaryen

No one had made a Lizards spam bot either, so I did that too. Rains fire down on her enemies.

puts 'L'

Run as

ruby Khaleesi.rb

Dr R Dizzle

Posted 2014-07-25T15:39:06.563

Reputation: 213

:D I almost would have posted exactly that in the beginning of the challenge, but then I went for Vulcan instead. – Martin Ender – 2014-07-29T11:42:27.637

@MartinBüttner Might add in the missing Lizard spammer as well, but I can't think of a terrible pop culture reference just yet. – Dr R Dizzle – 2014-07-29T11:43:32.630

1How about Sam Neill/Alan Grant? :P ... There are probably better options though. – Martin Ender – 2014-07-29T11:49:45.010

1Yeah. Daenerys is more dragon than lizard. Kaiju maybe ;-) – Eoin Campbell – 2014-07-29T12:51:50.983

1@EoinCampbell Carry on complaining and I'll make you run it as DaenerysTargaryenStormbornKhaleesiQueenOfTheAndalsAndTheFirstMenBreakerOfChains.... :P – Dr R Dizzle – 2014-07-30T11:32:41.313

1I'd much prefer long names than long run times... god damn casinoshakespeare has an avg. decision time of 0.5s while it calls out to a external url :-) – Eoin Campbell – 2014-07-30T12:39:09.717

3

Gazzr

This bot looks at the played games and tries to guess how to beat the enemy:

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class Gazzr {

    private static final int DEPTH = 3;
    private final List<String> moves = Arrays.asList(new String[] { "R", "L", "V", "S", "P" });
    private final String[] beatIndex = new String[] { "PV", "RS", "PL", "RV", "LS" };
    private final List<String> loseFromIndex = Arrays.asList(new String[] { "LS", "PV", "RS", "PL", "RV" });
    private final Random random = new Random();

    public static void main(final String[] args) {
        System.out.println(new Gazzr().play(args));;
    }

    public String play(final String[] args) {
        if (args == null || args.length == 0 || args[0].length() < DEPTH) {
            return moves.get(random.nextInt(moves.size()));
        }
        final Map<String, float[]> oppHistDb = new HashMap<String, float[]>();
        final Map<String, float[]> myHistDb = new HashMap<String, float[]>();
        final char[] oppMoves = args[1].toCharArray();
        int i = DEPTH;
        while(i < args[0].length()) {
            updateDB(args[0].substring(i-DEPTH, i), oppMoves[i], myHistDb);
            updateDB(args[1].substring(i-DEPTH, i), oppMoves[i], oppHistDb);
            i++;
        }
        //Predict based on own and opponent moves:
        Integer p1 = predict(args[0].substring(i-DEPTH, i), myHistDb);
        Integer p2 = predict(args[1].substring(i-DEPTH, i), oppHistDb);
        if(p1 == null && p2 == null) {
            return moves.get(random.nextInt(moves.size()));
        }
        if(p2 == null) {
            return "" + beatIndex[p1].charAt(random.nextInt(2));
        } else if(p1 == null) {
            return "" + beatIndex[p2].charAt(random.nextInt(2));
        }
        String s1 = moves.get(p1)+moves.get(p2);
        String s2 = moves.get(p2)+moves.get(p1);
        if(loseFromIndex.contains(s1)) {
            return moves.get(loseFromIndex.indexOf(s1));
        }
        if(loseFromIndex.contains(s2)) {
            return moves.get(loseFromIndex.indexOf(s2));
        }
        if(random.nextBoolean()) {
            return ""+ beatIndex[p1].charAt(random.nextInt(2));
        } else {
            return ""+ beatIndex[p2].charAt(random.nextInt(2));
        }
    }

    private void updateDB(String lastMoves, char nextMove, Map<String, float[]> database) {
        int oppIndex = moves.indexOf("" + nextMove);
        float[] distr = database.get(lastMoves);
        if(distr == null) {
            distr = new float[5];
        }
        distr[oppIndex] += 1.0f;
        database.put(lastMoves, distr);
    }

    public Integer predict(String historicMoves, Map<String, float[]> database) {
        final float[] distr = database.get(historicMoves);
        if(distr == null) {
            return null;
        }
        return predictFromArray(distr);
    }

    private Integer predictFromArray(final float[] distr) {
        float total = 0f;
        for(int i = 0;i<distr.length;i++) {
            total += distr[i];
        }
        float target = total * random.nextFloat();
        total = 0f;
        for(int i = 0;i<distr.length;i++) {
            total += distr[i];
            if(total > target) {
                return i;
            }
        }
        return distr.length - 1;
    }
}

Bug fixes:

  • Tournament round #1: Bot came in last, a bug was causing it to try to lose instead of win
  • (X beats Y, bot predicts opponent plays X, play Y!) #epic

Roy van Rijn

Posted 2014-07-25T15:39:06.563

Reputation: 1 082

1I've never really explained how it works...! The biggest advantage this bot has over most RPS-based bots is that it has two separate predictors, one looks at our history, the other on the opponents history. Now instead of picking the best prediction and then choosing the beating strategy I do something else. I leave both predictors to do their job and pick the single RPSLS strategy that beats both predicted moves in case the predictors don't agree (!). – Roy van Rijn – 2015-01-07T16:01:49.617

harsh dude :-) I'll update your source later and include it in the next round. – Eoin Campbell – 2014-08-01T13:32:59.147

2

Nitpicker (Ruby)

BotName: Nitpicker
Run:     ruby nitpicker.rb [Mine] [Theirs]

Code Update: Now counts more stuff.

Code

scores = [ 0 ] * 9

@moves = { "R" => 0, "P" => 1, "S" => 2, "L" => 3, "V" => 4 }
@responses = [ "R", "P", "S", "L", "V" ]
@counters = { "R" => [ "P", "V" ], "P" => [ "S", "L" ], "S" => [ "V", "R" ], "L" => [ "R", "S" ], "V" => [ "P", "L" ] }

def try( theirs, mine )
  return @counters[theirs].include? mine
end

def counter( move )
  return @counters[move][rand(2)]
end

def follower( move )
  return @responses[(@moves[move]+1)%5]
end

def echo( move )
  return move
end

if ARGV[1] != nil
  for i in 2 .. ARGV[1].length - 1
    points = i > ARGV[1].length - 10 ? 10 - ( ARGV[1].length - i ) : 1
    if try( ARGV[1].split("")[i], counter( ARGV[0].split("")[i-1] ))
      scores[0] += points
    end
    if try( ARGV[1].split("")[i], follower( ARGV[0].split("")[i-1] ))
      scores[1] += points
    end
    if try( ARGV[1].split("")[i], echo( ARGV[0].split("")[i-1] ))
      scores[2] += points
    end
    if try( ARGV[1].split("")[i], counter( ARGV[1].split("")[i-1] ))
      scores[3] += points
    end
    if try( ARGV[1].split("")[i], follower( ARGV[1].split("")[i-1] ))
      scores[4] += points
    end
    if try( ARGV[1].split("")[i], echo( ARGV[1].split("")[i-1] ))
      scores[5] += points
    end
    if try( ARGV[1].split("")[i], counter( ARGV[1].split("")[i-2] ))
      scores[6] += points
    end
    if try( ARGV[1].split("")[i], follower( ARGV[1].split("")[i-2] ))
      scores[7] += points
    end
    if try( ARGV[1].split("")[i], echo( ARGV[1].split("")[i-2] ))
      scores[8] += points
    end
  end
end

bestId = -1;
bestScore = 0;

for j in 0 .. scores.length - 1
  if scores[j] > bestScore
    bestScore = scores[j] 
    bestId = j
  end
end    

if bestId == 0
  puts counter( ARGV[0].split("")[-1] )
elsif bestId == 1
  puts follower( ARGV[0].split("")[-1] )
elsif bestId == 2
  puts echo( ARGV[0].split("")[-1] )
elsif bestId == 3
  puts counter( ARGV[1].split("")[-1] )
elsif bestId == 4
  puts follower( ARGV[1].split("")[-1] )
elsif bestId == 5
  puts echo( ARGV[1].split("")[-1] )
elsif bestId == 6
  puts counter( ARGV[1].split("")[-2] )
elsif bestId == 7
  puts follower( ARGV[1].split("")[-2] )
elsif bestId == 8
  puts echo( ARGV[1].split("")[-2] )
else
  puts @responses[rand(5)]
end

Thaylon

Posted 2014-07-25T15:39:06.563

Reputation: 1 324

1Some Ruby tips: • $moves, $responses, and $counters are global variables, but they would make more sense as constants, like MOVES, etc. • The Ruby convention for indent size is 2 spaces (and most other languages use 4 spaces), so 8-space indents for Ruby code are hard to read because they're unusual. • You can simplify the for loop by writing ARGV[1].split("").each_cons do |one_move_ago, two_moves_ago| ... end, which uses Enumerable#each_cons. • Code using $scores would be more readable if the $scores keys were symbols like :counter instead of integers like 0. – Rory O'Kane – 2014-07-25T21:44:11.703

Oh well, you can tell that its my first time with ruby ;) Thanks for the pointers, Ill look into that. However, conerning the for loop, mine excludes the zero'th element, your split includes it i believe? – Thaylon – 2014-07-25T22:37:37.727

Whoops, I meant to write each_cons(2), not each_cons – and I got the argument order wrong. The loop should be ARGV[1].split("").each_cons(2) do |two_moves_ago, one_move_ago| … end. With that, one_move_ago will never be assigned to the zeroth element of ARGV[1]. As an aside, if you needed to prevent even two_moves_ago from being the zeroth element, you could replace ARGV[1] with ARGV[1][1..-1]. 1..-1 is a range, and when used as an index, it means from the second element 1 to the last element -1. – Rory O'Kane – 2014-07-25T23:07:10.210

Some more tips I didn’t have space for in one comment: • Here’s a link to the docs for the aforementioned Enumerable#each_cons. • You can change the global variable $scores to just a local variable scores. It will still be accessible anywhere in the current scope, and since it’s defined at the top level, that means it will be accessible everywhere. • $responses[rand(5)] can be $responses.sample, using Array#sample.

– Rory O'Kane – 2014-07-25T23:11:17.727

2

YAAR (Yet Another Arcane Run-time)

If Google's V8 JavaScript Engine is installed (this all depends how far is @eoin-campbell willing to go? ;) )

jsBot.js:

var HAND=["R", "P", "S", "L", "V"];
print(HAND[Math.floor(Math.random() * 5)]);

and run it like so:

d8 jsBot.js

If compiling V8 is too much effort, Node.JS would also be able to run this, provided that print is changed to console.log.

The Spidermonkey jsshell would also work without modifications (precompiled builds on Mozilla's FTP server).

mccannf

Posted 2014-07-25T15:39:06.563

Reputation: 151

Sheldor says "Who's this trouble maker"... leave it with me... I'll see if I can coax him round ;0) – Eoin Campbell – 2014-07-25T19:54:42.297

I'll set it up with node later. cheers – Eoin Campbell – 2014-07-28T13:25:56.170

2

RandomlyWeighted (Ruby)

Bot name: RandomlyWeighted.

Run it with ruby randomly_weighted_rpslv.rb. Tested to work with Ruby versions 1.8.7, 2.0.0, and 2.1.2.

In the first round, this bot randomly decides which of the options it likes most and least – you can think of it as choosing its personality or its priorities. The bot assigns each of the five options a unique probability from the list [0.1, 0.1, 0.2, 0.2, 0.4]. In all rounds after the first, it sticks to those same probabilities when choosing what option to throw.

The bot stores its chosen probabilities in a file priorities.marshal-rb in the Data folder.

randomly_weighted_rpslv.rb
DATA_FOLDER_PATH = begin
  dir_of_this_program = File.expand_path(File.dirname(__FILE__))
  File.join(dir_of_this_program, "Data")
end

module Priorities
  PRIORITIES_FILE_PATH = File.join(DATA_FOLDER_PATH, "priorities.marshal-rb")

  def self.priorities
    begin
      load_saved_priorities
    rescue Errno::ENOENT => e
      # file doesn't exist yet; it's the first round
      chosen_priorities = choose_priorities_randomly
      save_priorities(chosen_priorities)
      chosen_priorities
    rescue IOError => e
      $stderr.puts "Error: priorities file exists, but could not read it."
      $stderr.puts "Fix its permissions or something."
      exit(1)
    end
  end

  private

  def self.choose_priorities_randomly
    probability_spread = [0.1, 0.1, 0.2, 0.2, 0.4]
    options = ['R', 'P', 'S', 'L', 'V']
    paired_options_and_probs = options.shuffle.zip(probability_spread)
    Hash[paired_options_and_probs]
  end

  def self.load_saved_priorities
    File.open(PRIORITIES_FILE_PATH, 'r') do |priorities_file|
      Marshal.load(priorities_file)
    end
  end

  def self.save_priorities(priorities_to_save)
    Dir.mkdir(DATA_FOLDER_PATH)
    File.open(PRIORITIES_FILE_PATH, 'w') do |priorities_file|
      Marshal.dump(priorities_to_save, priorities_file)
    end
  end
end

def prioritized_random_choice(priorities)
  # precondition: the probabilities in `priorities` add up to 1

  remaining_probability_needed = rand
  priorities.to_a.shuffle.each do |option, probability|
    remaining_probability_needed -= probability
    if remaining_probability_needed < 0
      return option
    end
  end
end

puts prioritized_random_choice(Priorities.priorities)

Rory O'Kane

Posted 2014-07-25T15:39:06.563

Reputation: 145

error when running this one. D:/My Software Dev/big-bang-game/BigBang.Orchestrator/bin/Debug/Players/RandomlyWeighted/RandomlyWeighted.rb:2:in <module:Priorities>': uninitialized constant Priorities::DATA_FOLDER_PATH (NameError) from D:/My Software Dev/big-bang-game/BigBang.Orchestrator/bin/Debug/Players/RandomlyWeighted/RandomlyWeighted.rb:1:in<main>' – Eoin Campbell – 2014-07-26T00:13:42.973

2

Might As Well Be Random

Yeah, I wouldn't expect much from this guy. Never have I ever written a perl script this long before. That's because I wasn't using half of the code :P

use strict;
use warnings;
use Carp qw(croak);

###Stolen from List::Util::ChooseWeighted (with love)
sub choose_weighted{
    my ($objects, $weightsArg ) = @_;
    my $calcWeight = $weightsArg if 'CODE' eq ref $weightsArg;
    my @weights;        # fix wasteful of memory
    if( $calcWeight){
    @weights =  map { $calcWeight->($_) } @$objects;
    }
    else{
    @weights =@$weightsArg;
    if ( @$objects != @weights ){
        croak "given arefs of unequal lengths!";
    }
    }

    my @ranges = ();        # actually upper bounds on ranges
    my $left = 0;
    for my $weight( @weights){
    $weight = 0 if $weight < 0; # the world is hostile...
    my $right = $left+$weight;
    push @ranges, $right;
    $left = $right;
    }
    my $weightIndex = rand $left;
    for( my $i =0; $i< @$objects; $i++){
    my $range = $ranges[$i];
    return $objects->[$i] if $weightIndex < $range;
    }
}
###Credit to Danny Sadinof for choose_weighted code

sub getmove {
    my (@opponenthistory) = @_;
    if(~~@opponenthistory == 0) {
        return ~~(5*rand());
    }
    my %mapper = qw(R 0 P 1 S 2 V 3 L 4);
    @opponenthistory = map {$mapper{$_}} @opponenthistory;
    my %counts = (
        0 => 0,
        1 => 0,
        2 => 0,
        3 => 0,
        4 => 0);
    $counts{$_}++ for @opponenthistory;
    my @keys = keys %counts;
    my @values = values %counts;
    my $choice = choose_weighted(\@keys, \@values);
    return ($choice + 1) % 4;
}

if(~~@ARGV == 0) {
    print qw/R P S V L/[~~(5*rand())];
    exit;
}
my ($myhistory, $opponenthistory) = @ARGV;

print qw/R P S V L/[getmove(split //, $opponenthistory)];

run as

perl mightaswellberandom.pl

killmous

Posted 2014-07-25T15:39:06.563

Reputation: 369

1Renamed as "MAWBRBot" :-) – Eoin Campbell – 2014-07-27T19:30:44.187

2

Herpetologist

This one's in Python 3. He really likes lizards for some reason.

from sys import argv
from random import choice
print("PLSVR"[len(argv)<2 or "RVPSL".index(sorted([choice(argv[2]) for i in range(12)], key=lambda *a:argv[1].count(a[0]))[choice([0, 1])])])

cjfaure

Posted 2014-07-25T15:39:06.563

Reputation: 4 213

2

CasinoShakespeare

Description:

Shakespeare wants to be famous so he feels ok with playing your game. He needs a moment to recall something wise from memory(aka website with quotes) so please don't be too hard on him.

Code:

using System;
using System.Linq;
using System.Net;

namespace CasinoShakespeare
{
    class CasinoShakespeare
    {
        private static void Main()
        {
            try
            {
                char[] ar = {'R', 'P', 'S', 'L', 'V'};
                var a = new WebClient().DownloadString("http://www.iheartquotes.com/api/v1/random").Select(char.ToUpperInvariant).FirstOrDefault(ar.Contains);
                Console.WriteLine(a == default(char) ? 'S' : a);
            }
            catch (Exception)
            {
                Console.WriteLine("R");
            }
        }
    }
}

First compile with: csc.exe CasinoShakespeare.cs then run: CasinoShakespeare.exe

Daniel Kmak

Posted 2014-07-25T15:39:06.563

Reputation: 121

2

GitGudBot

class Progressive
    def main
        args = CobraCore.commandLineArgs
        rng = Random()
        if args.count == 1, Console.write('RPSLV'[rng.next(5)])
        else
            them = List<of char>(args[2].toCharArray)
            me = List<of char>(args[1].toCharArray)
            options = List<of char>()
            for num in me.count
                if '[me[num]][them[num]]' in ['SP','PR','RL','LV','VS','SL','PV','RS','LP','VR'], options.add(me[num])
                else if '[them[num]][me[num]]' in ['SP','PR','RL','LV','VS','SL','PV','RS','LP','VR'], options.add(them[num])
                else, pass
            while options.count > 1
                options.reverse
                if rng.next(3) == 0, break
                else, options.pop
            Console.write(if(options.count,options.last,'RPSLV'[rng.next(5)]))

Written in Cobra, which should be easy to install if you've already got a Visual Studio package on the machine. If not, install any VS package, then install Cobra, then add C:\Cobra\bin to your %PATH%. If you don't feel like changing your %PATH%, simply copy cobra.bat from C:\Cobra\bin to the directory with the source file in it.

Compile & Run: cobra GitGudBot -- [arg1] [arg2]

-OR-

Compile: cobra GitGudBot

Run: GitGudBot [arg1] [arg2]

Οurous

Posted 2014-07-25T15:39:06.563

Reputation: 7 916

2

Bart

@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
  when 0..19
    'R'
  when 20..30
    opponent.count('P') > opponent.count('V') ? 'S' : 'L'
  else
    extrapolate(player, opponent)
  end

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(:+)
  end
end

puts move

Ruby, run with ruby bart.rb. Good old rock, nothing beats it.

histocrat

Posted 2014-07-25T15:39:06.563

Reputation: 20 600

Does this only return rock? – None – 2014-07-28T01:59:54.957

1Bart wises up eventually. – histocrat – 2014-07-28T03:39:24.147

2

Alternator

ruby alternator.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
  i = player.to_s.size
  case
  when i < 5
    @moves[player.to_s.size]
  when i.even?
    @moves.shuffle.min_by{|m|player.count(m)}
  else
    extrapolate(player, opponent)
  end

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(:+)
  end
end

puts move

On odd numbered moves, tries to predict the opponent. On even numbered moves, tries to be unpredictable.

histocrat

Posted 2014-07-25T15:39:06.563

Reputation: 20 600

you're gonna burn out my machine dude :-) – Eoin Campbell – 2014-07-28T01:35:31.883

Heh, sorry. Don't worry, I'm done. – histocrat – 2014-07-28T01:40:44.493

kidding man. keep em coming. you're keeping my on my toes :-) – Eoin Campbell – 2014-07-28T01:49:59.733

2

Q - Cobra

Named after the near-omnipotent being(s) in the Star Trek universe, Q plays the long game, makes no mistakes, and watches everything.

For the first game in a tournament run, it will choose a random option.

For the first game in each round, it will choose the option most likely to win against all moves previously played in the tournament. If two options are equal in likelihood, it will choose the least used one. If there is still a tie, it will randomly select from the remaining choices.

For every other game in each round, the following selection algorithm is used:

A = Chance of the current opponent choosing the given move in response to my last move
B = Certainty that the opponent's choices of the given move are dependent on my moves
X = Chance of any opponent choosing the given move in response to my last move
Y = Certainty that any opponent's choices of the given move are dependent on my moves
M = Current game number
N = Win/lose/tie against the given choice (+2,+1,-2)

Move weighting = A*B*N*M/10 + X*Y*N

Breaking ties with the least selected option, and breaking secondary ties with Random()

(uses uint64 in case the round-lengths become rather large)

@number float
class Bot
    def main
        index = {c'R':0,c'P':1,c'S':2,c'L':3,c'V':4}
        win = @[@[2,3],@[0,4],@[1,3],@[1,4],@[0,2]]
        lose = @[@[1,4],@[2,3],@[0,4],@[0,2],@[1,3]]
        args = CobraCore.commandLineArgs
        rng = Random()
        path = CobraCore.exePath.split('\\')[:-1].join('\\')+'\\data'
        if not Directory.exists(path), Directory.createDirectory(path)
        past_history = [List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5))]
        past_frequency = List<of uint64>(uint64[](5))
        if File.exists(path+'\\history.txt')
            new_game = false
            for num, line in File.readAllLines(path+'\\history.txt').numbered
                for n in 5
                    past_history[num][n] = uint64.parse(line.split[n])
                    past_frequency[n] += uint64.parse(line.split[n])
        else, new_game = true
        if new_game, Console.write('RPSLV'[rng.next(5)])
        else if args.count <> 3 or if(args.count<>3,false,args[2].length<2)
            options = List<of float>(float[](5))
            for n in 5
                all_weight = past_frequency[n]/if(.sum(past_frequency),.sum(past_frequency),1)
                for m in win[n]
                    options[m] += all_weight*2
                options[n] += all_weight
                for m in lose[n]
                    options[m] -= all_weight*2
            temp_selection = List<of int>()
            for num, o in options.numbered
                if o == options.sorted.last
                    temp_selection.add(num)
            Console.write('RPSLV'[temp_selection[rng.next(temp_selection.count)]])
        else
            this_history = [List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5))]
            this_frequency = List<of uint64>(uint64[](5))
            old_history = [List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5))]
            old_frequency = List<of uint64>(uint64[](5)) 
            if args[1].length > 10
                for num, move in args[1][:args[1].length-11].numbered
                    old_history[index[move]][index[args[2][num+1]]] += 1
                    old_frequency[index[args[2][num+1]]] += 1
                for num, move in args[1][args[1].length-11:-1].numbered
                    this_history[index[move]][index[args[2][num+1]]] += 1
                    this_frequency[index[args[2][num+1]]] += 1
            else
                for num, move in args[1][:-1].numbered
                    this_history[index[move]][index[args[2][num+1]]] += 1
                    this_frequency[index[args[2][num+1]]] += 1
            me_last = index[args[1][args[1].length-1]]
            them_last = index[args[2][args[2].length-1]]
            turns = args[1].length
            options = List<of float>(float[](5))
            for n in 5
                this_rchan = this_history[me_last][n]/if(.sum(this_history[me_last]),.sum(this_history[me_last]),1)
                this_rcert = this_history[me_last][n]/(if(this_frequency[me_last],this_frequency[me_last],1)/5f)
                this_weight = (this_rchan*this_rcert)*if(turns>10,2f,turns/5)
                old_rchan = old_history[me_last][n]/if(.sum(old_history[me_last]),.sum(old_history[me_last]),1)
                old_rcert = old_history[me_last][n]/(if(old_frequency[me_last],old_frequency[me_last],1)/5f)
                old_weight = (old_rchan*old_rcert)/if(20-turns>0,20-turns,1)
                all_rchan = past_history[me_last][n]/if(.sum(past_history[me_last]),.sum(past_history[me_last]),1)
                all_rcert = past_history[me_last][n]/(if(past_frequency[me_last],past_frequency[me_last],1)/5f)
                all_weight = (all_rchan*all_rcert)/(Math.log(turns)/1.5f)
                for m in win[n]
                    options[m] -= this_weight*2 + old_weight*2 + all_weight*2
                options[n] -= this_weight + old_weight + all_weight
                for m in lose[n]
                    options[m] += this_weight*2 + old_weight*2 + all_weight*2
            temp_selection = List<of int>()
            for num, o in options.numbered
                if o == options.sorted.last
                    temp_selection.add(num)
            Console.write('RPSLV'[temp_selection[rng.next(temp_selection.count)]])
            past_history[index[args[1][args[1].length-2]]][them_last] += 1

        # write the history file
        record = List<of String>()
        for entry in past_history
            record.add(entry.join(' '))
        File.writeAllLines(path+'\\history.txt',record)

    def sum(list as List<of uint64>) as uint64
        num = 0u64
        for entry in list, num += entry
        return num

Written in Cobra, which should be easy to install if you've already got a Visual Studio package on the machine. If not, install any VS package, then install Cobra, then add C:\Cobra\bin to your %PATH%. If you don't feel like changing your %PATH%, simply copy cobra.bat from C:\Cobra\bin to the directory with the source file in it.

Compile & Run: cobra Q -- [arg1] [arg2]

-OR-

Compile: cobra Q

Run: Q [arg1] [arg2]

NOTE: to update to a newer version of the bot, delete the old executable before compiling

Οurous

Posted 2014-07-25T15:39:06.563

Reputation: 7 916

1+1 for the name, but I have no idea what it does. – Martin Ender – 2014-07-28T08:23:55.603

@MartinBüttner Explanation is up now! :) – Οurous – 2014-07-28T08:44:46.210

2

Java - DogeBot

Basically just looks for a pattern of two consecutive hands and beats it. If it can't find a pattern, it just beats whatever the last hand was. And if the number of last hands is less than two or it's a new game, it just prints P.

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

        if (args.length < 2) {
            System.out.println("P");
            return;
        }

        String yourHistory = args[1];  

        if (yourHistory.length() < 2) {
            System.out.println("P");
            return;
        }    

        String possibleHands = "RPSLV";

        for (int i = 0; i < 5; i++) {
            String searchString = "" + possibleHands.charAt(i) + possibleHands.charAt(i);

            if (yourHistory.contains(searchString)) {

                char newHand = possibleHands.charAt((i + 2) % 5);
                System.out.println(newHand == 'S' ? "P" : newHand);

                return;
            } else continue;

        }

        char lastHistoryHand = yourHistory.charAt(yourHistory.length() - 1);
        int i = possibleHands.indexOf(lastHistoryHand);

        char newHand = possibleHands.charAt((i + 2) % 5);
        System.out.println(newHand == 'S' ? "P" : newHand);

        return;
    }
}

Milo

Posted 2014-07-25T15:39:06.563

Reputation: 3 082

I just realized how dumb my bot is. Once it finds a pattern, it will never play another hand again. Making a 2.0. – Milo – 2014-07-29T03:21:50.160

2

Deja_Q - Cobra

Similar to the first Q, but it doesn't record history of past matches. Fares much better against opponents that attempt to analyse its patterns.

@number float
class Bot
    def main
        index = {c'R':0,c'P':1,c'S':2,c'L':3,c'V':4}
        win = @[@[2,3],@[0,4],@[1,3],@[1,4],@[0,2]]
        lose = @[@[1,4],@[2,3],@[0,4],@[0,2],@[1,3]]
        args = CobraCore.commandLineArgs
        rng = Random()
        if args.count <> 3
            Console.write('RPSLV'[rng.next(5)])
        else if args[2].length < 2
            Console.write('RPSLV'[lose[index[args[2][0]]][rng.next(2)]])
            options = List<of float>(float[](5))
        else
            this_history = [List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5))]
            this_frequency = List<of uint64>(uint64[](5))
            old_history = [List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5))]
            old_frequency = List<of uint64>(uint64[](5)) 
            if args[1].length > 10
                for num, move in args[1][:args[1].length-11].numbered
                    old_history[index[move]][index[args[2][num+1]]] += 1
                    old_frequency[index[args[2][num+1]]] += 1
                for num, move in args[1][args[1].length-11:-1].numbered
                    this_history[index[move]][index[args[2][num+1]]] += 1
                    this_frequency[index[args[2][num+1]]] += 1
            else
                for num, move in args[1][:-1].numbered
                    this_history[index[move]][index[args[2][num+1]]] += 1
                    this_frequency[index[args[2][num+1]]] += 1
            me_last = index[args[1][args[1].length-1]]
            them_last = index[args[2][args[2].length-1]]
            turns = args[1].length
            options = List<of float>(float[](5))
            for n in 5
                this_rchan = this_history[me_last][n]/if(.sum(this_history[me_last]),.sum(this_history[me_last]),1)
                this_rcert = this_history[me_last][n]/(if(this_frequency[me_last],this_frequency[me_last],1)/5f)
                this_weight = this_rchan*this_rcert*2
                old_rchan = old_history[me_last][n]/if(.sum(old_history[me_last]),.sum(old_history[me_last]),1)
                old_rcert = old_history[me_last][n]/(if(old_frequency[me_last],old_frequency[me_last],1)/5f)
                old_weight = old_rchan*old_rcert    
                for m in win[n]
                    options[m] -= this_weight*2 + old_weight*2
                options[n] -= this_weight + old_weight
                for m in lose[n]
                    options[m] += this_weight*2 + old_weight*2
            temp_selection = List<of int>()
            for num, o in options.numbered
                if o == options.sorted.last
                    temp_selection.add(num)
            Console.write('RPSLV'[temp_selection[rng.next(temp_selection.count)]])

    def sum(list as List<of uint64>) as uint64
        num = 0u64
        for entry in list, num += entry
        return num

Compile & Run: cobra Deja_Q -- [arg1] [arg2]

-OR-

Compile: cobra Deja_Q

Run: deja_q [arg1] [arg2]

Οurous

Posted 2014-07-25T15:39:06.563

Reputation: 7 916

2

IHaveNoIdeaWhatImDoing - Common Lisp (SBCL-specific)

BotName: IHaveNoIdeaWhatImDoing
Compile: sbcl --load rpsvl.lisp --eval "(sb-ext:save-lisp-and-die \"rpsvl.exe\" :toplevel #'main :executable t)"
Run:     rpsvl.exe [MyArgs] [HisArgs]

Code:

;; Utility ;;
;;;;;;;;;;;;;

(defconstant +possible-moves+ '((paper . #\p) (scissors . #\s) (rock . #\r) (lizard . #\l) (spock . #\v)))

;; Split input string at delim
(defun split-string (string delim)
  (loop for i = 0 then (1+ j)
       as j = (position delim string :start i)
       collect (subseq string i j)
       while j))

(defun get-move (ch)
  (let ((early-move (car (rassoc (char-downcase ch) +possible-moves+))))
    (if (not early-move)
        'forfeit
        early-move)))

;; Take input string and turn into list of symbols for moves
(defun get-moves (string)
  (let ((move-list (coerce string 'list)))
    (loop for ch in move-list
         collect (get-move ch))))

;; Get his moves
(defun get-args ()
  (get-moves (caddar (split-string *posix-argv* #\Space))))

;; Game Logic ;;
;;;;;;;;;;;;;;;;

;; Randomly select move from list
(defun get-random-move ()
  (cdr
   (nth
    (random (list-length +possible-moves+))
    +possible-moves+)))

;; Return list of move counts for input moveset
(defun count-moves (move-set)
  (let ((keys (remove-duplicates move-set)))
    (let ((counts
           (loop for key in keys
                collect (count key move-set))))
      (mapcar #'list keys counts))))

;; Select highest move used from previous moves ignoring ties
(defun max-move (move-list)
  (loop for move in move-list
       with max = (car move-list)
       when (< (cadr max) (cadr move)) do (setf max move)
       finally (return (car max))))

;; Make an educated guess at their next input
(defun parse-input (move-set)
  (let ((move (max-move (count-moves move-set))))
    (cond
      ((eq 'paper    move) #\L)
      ((eq 'scissors move) #\R)
      ((eq 'rock     move) #\V)
      ((eq 'lizard   move) #\S)
      ((eq 'spock    move) #\P)
      (t (get-random-move)))))

;; Main Function ;;
;;;;;;;;;;;;;;;;;;;
(defun main ()
  ;; Reseed RNG
  (setf *random-state* (make-random-state t))
  (let ((input (get-args)))
    (if (< (list-length input) 3)
        (princ (char-upcase (get-random-move)))
        (princ (char-upcase (parse-input input))))))

First time submitting, please let me know if I did something not to expectations. You can compile this just using Steel Bank Common Lisp REPL after loading it or by using buildapp. I can also provide a binary if you want.

Let me know what you guys think, thanks!

NonFunctional User29916

Posted 2014-07-25T15:39:06.563

Reputation: 21

Welcome to Programming Puzzles & Code Golf Stack Exchange. You seem to be doing everything right, though if the OP can't run LISP that binary you mentioned might be necessary, so keep in touch. Have a good time at the site! – isaacg – 2014-07-29T05:48:40.070

Thanks for the submission. will have a look at adding lisp support to the orchestration engine this evening. :-) – Eoin Campbell – 2014-07-29T07:52:58.230

@EoinCampbell I updated the compile command to be a one liner if that helps you. – NonFunctional User29916 – 2014-07-30T05:00:47.563

yep. that worked fine once I figured out I needed to set a %SBCL_HOME% env variable. It's incl. in the current run. – Eoin Campbell – 2014-07-31T20:29:14.513

2

QQ - Cobra

@number float

class Bot

    def main

        index = {c'R':0,c'P':1,c'S':2,c'L':3,c'V':4}
        win = @[@[2,3],@[0,4],@[1,3],@[1,4],@[0,2]]
        lose = @[@[1,4],@[2,3],@[0,4],@[0,2],@[1,3]]
        args = CobraCore.commandLineArgs
        rng = Random()
        path = CobraCore.exePath.split('\\')[:-1].join('\\')+'\\data'

        #ensure data directory exists
        if not Directory.exists(path), Directory.createDirectory(path)

        #read history from the data directory, and check if it's a new tournament
        past_history = [List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5))]
        past_frequency = List<of uint64>(uint64[](5))
        if File.exists(path+'\\history.txt')
            new_game = false
            for num, line in File.readAllLines(path+'\\history.txt').numbered
                for n in 5
                    past_history[num][n] = uint64.parse(line.split[n])
                    past_frequency[n] += uint64.parse(line.split[n])
        else, new_game = true

        #make the move
        this_history = [List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5)),List<of uint64>(uint64[](5))]
        this_frequency = List<of uint64>(uint64[](5))
        if new_game, Console.write('RPSLV'[rng.next(5)])
        else if args.count <> 3
            options = List<of float>(float[](5))
            for n in 5
                all_weight = past_frequency[n]/if(.sum(past_frequency),.sum(past_frequency),1)
                for m in win[n]
                    options[m] += all_weight*2
                options[n] += all_weight
                for m in lose[n]
                    options[m] -= all_weight*2
            temp_selection = List<of int>()
            for num, o in options.numbered
                if o == options.sorted.last
                    temp_selection.add(num)
            Console.write('RPSLV'[temp_selection[rng.next(temp_selection.count)]])
        else
            for num, move in args[1].numbered
                this_history[index[move]][index[args[2][num]]] += 1
                this_frequency[index[move]] += 1
                this_frequency[index[args[2][num]]] += 1
            me_last = index[args[1][args[1].length-1]]
            them_last = index[args[2][args[2].length-1]]
            turns = args[1].length
            options = List<of float>(float[](5))
            for n in 5
                this_rchan = this_history[me_last][n]/if(.sum(this_history[me_last]),.sum(this_history[me_last]),1)
                this_rcert = this_history[me_last][n]/(if(this_frequency[me_last],this_frequency[me_last],1)/5f)
                this_weight = this_rchan*this_rcert*turns/10
                all_rchan = past_history[me_last][n]/if(.sum(past_history[me_last]),.sum(past_history[me_last]),1)
                all_rcert = past_history[me_last][n]/(if(past_frequency[me_last],past_frequency[me_last],1)/5f)
                all_weight = all_rchan*all_rcert
                for m in win[n]
                    options[m] -= this_weight*2 + all_weight*2
                options[n] -= this_weight + all_weight
                for m in lose[n]
                    options[m] += this_weight*2 + all_weight*2
            temp_selection = List<of int>()
            for num, o in options.numbered
                if o == options.sorted.last
                    temp_selection.add(num)
            Console.write('RPSLV'[temp_selection[rng.next(temp_selection.count)]])
            past_history[me_last][them_last] += 1

        #update the history file
        record = List<of String>()
        for entry in past_history
            record.add(entry.join(' '))
        File.writeAllLines(path+'\\history.txt',record)

    def sum(list as List<of uint64>) as uint64
        num = 0u64
        for entry in list, num += entry
        return num

Plays against it's best guess of the opponent's last move. Silly QQ...

Compile & Run: cobra QQ -- [arg1] [arg2]

-OR-

Compile: cobra QQ

Run: cobra qq

Οurous

Posted 2014-07-25T15:39:06.563

Reputation: 7 916

2

Concession bot - Python 2

The inverse of my other bot. Attempts to predict what move its opponent will play so that it can come up with the worst possible move.

import sys
import random

concessions = {"R": "LS", "P": "VR", "S": "PL", "L": "VP", "V": "SR"}
valid_letters = "RPSLV"

# Create a dictionary of all possible patterns of how the cause history could have affected the effect history's next letter
def generate_pattern_dict(cause_history, effect_history):
    dictionary = {}

    # Run through all the rounds so far
    for round in xrange(len(cause_history)):

        history = cause_history[:round][::-1]
        letter = effect_history[round]

        if not letter in dictionary:
            dictionary[letter] = {}

        # Loop through every size of a string that could fit in the history
        for hs_len in range(1, len(history) + 1):

            # Loop through every position a string of that size could be in
            for hs_pos in range(len(history) - hs_len + 1):

                # Add this occurance to the dictionary; noting how long before the letter it occured and what it was
                history_string = history[hs_pos:hs_pos + hs_len]
                dist = hs_pos
                try:
                    dictionary[letter][(dist, history_string)] += 1
                except KeyError:
                    dictionary[letter][(dist, history_string)] = 1
    return dictionary

# Given a pattern dictionary; predict the next letter based on a history
def get_probabilities(patterns, history):
    probs = {}
    history = history[::-1]

    # Look at all the letters that have been used
    for letter in patterns:
        probs[letter] = 0

        # Look at all the known string patterns to have preceded this string and mark this letter as more likely the more strings that point to it
        for key, occurrences in patterns[letter].iteritems():
            dist, history_string = key

            if history[dist: dist + len(history_string)] == history_string:
                probs[letter] += occurrences

    return probs

def get_most_probable(dictionary):
    highest_prob = 0
    hightst_letters = ""

    for letter, prob in dictionary.iteritems():
        if prob >= highest_prob:
            if prob > highest_prob:
                hightst_letters = letter
                highest_prob = prob
            else:
                hightst_letters += letter

    if len(hightst_letters) == 0:
        hightst_letters = valid_letters

    return random.choice(hightst_letters), highest_prob

def get_histories():
    try:
        return (sys.argv[1], sys.argv[2])
    except IndexError:
        return ("", "")

def concede(letter):
    return random.choice(concessions[letter])

def get_hand(my_history, opponent_history):

    # Look at the affect the opponent's moves have on what they choose next
    pattern_dict = generate_pattern_dict(opponent_history, opponent_history)
    pattern_probs = get_probabilities(pattern_dict, opponent_history)
    pattern_letter, pattern_prob = get_most_probable(pattern_probs)

    # Look at what affect my moves have on what the opponent chooses next
    prediction_dict = generate_pattern_dict(my_history, opponent_history)
    prediction_probs = get_probabilities(prediction_dict, my_history)
    prediction_letter, prediction_prob = get_most_probable(prediction_probs)

    if pattern_prob > prediction_prob:
        opponent_letter = pattern_letter
    elif pattern_prob < prediction_prob:
        opponent_letter = prediction_letter
    else:
        opponent_letter = random.choice((pattern_letter, prediction_letter))

    return concede(opponent_letter)


print get_hand(*get_histories())

Run with:

    python concession_bot.py <args>

Docopoper

Posted 2014-07-25T15:39:06.563

Reputation: 51

2

First time poster I'd like to submit the bot I wrote for this tournament:

The Evolver

BotName: Evolver
Compile: 
Run:     lua evolver.lua [StoryEvolver] [StoryOpponent]

and now the code:



-- The Evolver Bot
-- Vittorio "Robotik" Parrella - 01/08/2014
-- Version 3 02/08/2014

--[[
    os.time() gives time in seconds, so the RNG's seed is sometimes 
    the same across many executions, so we use os.clock() to mark
    a time offset (in microseconds.) to set the seed.
]]
function random_seed_microseconds()
    local start = os.clock()
    for i=1,math.random(5000000,40000000) do end -- Loop to let some microseconds pass

    local off = (os.clock() - start)*10000
    start = os.time()*1000

    math.randomseed( start+off )
end

DEBUG = false
DATAFILE = "data.lua"
WIN_WEIGHT  = 1.0
DRAW_WEIGHT = 0.2
LOSE_WEIGHT = 0.5
MAX_FITNESS_VALUE = WIN_WEIGHT + DRAW_WEIGHT

my_moves = arg[1] or ""
opponent_moves = arg[2] or "-"

--[[
  Print debug information
]]
function dprint( ... )
    if DEBUG then
        print( "------" )
        print( ... )
    end
end

--[[
    This bot makes use (kind-of) an evolutionary algorithm, 
    modifying the ranges used to randomly select the next
    symbol to play.

    The fitness function for the data ranges checks if a 
    given set of ranges can create a sequence of symbols
    that is sufficiently good to beat the opponent's plays,
    so that it can be used to actually select a symbol that 
    _could_ beat the next unkwown opponent symbol.
]]
function main()
    random_seed_microseconds()

    -- Initializes the bot.
    gen = new( genetic )

    -- Loads the data from the file or in
    -- case generates new data.
    -- FIX: at the start of a new match throws out the last
    -- datafile.
    if #my_moves < 1 or gen:load( DATAFILE ) == false then
        gen:generate( math.random( 10,20 ) )
    end

    -- Updates the fitness data with the new moves.
    gen:calculate_fitness( my_moves, opponent_moves )

    -- Selects and prints the next symbol using the 
    -- distribution intervals with maximum fitness.
    print( gen:getSymbol( gen.data[1] , opponent_moves ) )

    dprint( gen:print() )

    -- Saves the calculated evolutionary data.
    gen:save( DATAFILE )
end

--[[ 
   Matrix graph for the rules:
   0 = draw
   1 = symbol 1 wins
   2 = symbol 2 wins
 ]]
SYMBOLS = { "R","P","S","L","V" }
rules = {
    ["R"] = { ["R"]=0,["P"]=2,["S"]=1,["L"]=1,["V"]=2 },
    ["P"] = { ["R"]=1,["P"]=0,["S"]=2,["L"]=2,["V"]=1 },
    ["S"] = { ["R"]=2,["P"]=1,["S"]=0,["L"]=1,["V"]=2 },
    ["L"] = { ["R"]=2,["P"]=1,["S"]=2,["L"]=0,["V"]=1 },
    ["V"] = { ["R"]=1,["P"]=2,["S"]=1,["L"]=2,["V"]=0 },
}

dprint( my_moves, opponent_moves )

function limit( min, max, value )
    return math.max( min, math.min( max, value ) )
end

--[[
   Very simple object orientation
]]
function new( class )
    local t = {}

    for i,k in pairs( class ) do
        t[i] = k
    end

    return t
end

--[[
   Class for the genetic algorithm
]]
genetic = {
    -- Prints the rows of paramters in the object + the fitness value for the combination
    print = function( self )
        local str = ""
        for i,fit in pairs( self.data ) do
            str = str.."{ "..table.concat(fit, ",", 1, 5).." } [".. fit[6] .."]\n"
        end
        return str
    end,

    --[[ 
      Writes the bots current data to the file in a format easily
      loadable with Lua.
    ]]
    save = function ( self, filepath )
        local file = io.open( filepath, "w" )
        assert( not(file == nil), "file cannot be opened for write: "..filepath )

        file:write( "return {\n" )
        for _,fit in pairs( self.data ) do
            file:write( "  { " )
            for j=1,#fit do
                file:write( fit[j]..", " )
            end
            file:write( "},\n" )
        end
        file:write( "}\n" )

        file:close()
    end,

    --[[
        Tries to load in memory the evolutionary parameters data table 
        from the data.lua file.
    ]]
    load = function( self, filepath )
        result, self.data = pcall( dofile, filepath )

        if result == false then
            self.data = nil
        end

        return result
    end,

    --[[
        Uses the eveolutionary data row (should always be the max fitness one) to 
        choose the symbol to print using a non-uniform distribution.
    ]]
    getSymbol = function( self, data )
        local roll = math.random()
        for case=1,5 do
            if roll < data[ case ] then
                return SYMBOLS[ case ]
            end
        end
    end,

    --[[
        Calculates the fitness value for all the data rows in the bot
        in respect to the opponent's previous moves.
    ]]
    calculate_fitness = function( self, my_moves, opp_moves )
        local max_fitness = 0
        local performance = 5

        -- Evaluates the performance of the used data row, counting losses
        for i=#(my_moves:sub(-5)), 1,-1 do
            if rules[ my_moves:sub(-i,-i) ][ opp_moves:sub(-i,-i) ] == 2 then
                performance = performance - 1
            end
        end

        for i,row in pairs( self.data ) do
            -- If first row's perfomance is good, there's no need to mutate it.
            if i > 1 or performance < 3 then
                self:mutate( i )

                local wins = 0
                local draws = 0

                --[[
                    Here we basically check if every row would be capable of defeating
                    the last opponent's moves, so that it can be used to MAYBE
                    defeat it's next moves.
                ]]
                local size = #opp_moves
                for piv=size,1,-1 do
                    local sym = self:getSymbol( row ) -- A possible symbol calculated using this row
                    local opp_sym = opp_moves:sub( piv,piv ) -- The last move used by the opponent

                    local result = rules[ sym ][ opp_sym ]

                    if result == 0 then
                        draws = draws + 1
                    elseif result == 1 then
                        wins = wins + 1
                    end
                end

                local winning_state = (wins+draws) / size
                local draws_state = 1.0-(draws / size)
                local losing_state = (size-wins) / size

                --[[ 
                    Updates the fitness of the data row maximising victories+draws ( max 1.0 ).
                    Adds a bonus for the number of draws made ( max 0.4 )
                    Adds a malus for the number of losses ( max 0.4 )
                ]]
                self.data[i][6] = winning_state * WIN_WEIGHT 
                                + draws_state   * DRAW_WEIGHT 
                                - losing_state  * LOSE_WEIGHT

                if self.data[i][6] > max_fitness then
                    max_fitness = self.data[i][6]
                end
                dprint( wins, draws, self.data[i][6] )
            end
        end

        if max_fitness < MAX_FITNESS_VALUE * 0.7 then
            self:generate( math.random( 10, 20 ) )
        else
            -- Orders the rows so that the first row has the highest fitness
            table.sort( self.data, function( a,b ) return a[6] > b[6] end )
        end
    end,

    --[[ Initially generates all the evolutionary data rows ]]
    generate = function( self, size )
        self.data = {}

        for i=1,size do
            self.data[i] = { 0.2, 0.4, 0.6, 0.8 } -- An initial uniform distribution gives good results

            self.data[i][5] = 1 -- High limit for the random symbols intervals
            self.data[i][6] = 0 -- Starting fitness value
        end
    end,

    --[[
        Executes a mutation on the values of a specified data row,
        for now the mutation is just a modification of the values
        to 20% (increase or decrease) of their initial value.
    ]]
    mutate = function( self, index )
        for j=1,4 do
            local plus = 0

            -- randomly picks an increase or a decrease of 20% of the range.
            plus = self.data[index][j] * 0.2

            if math.random() < 0.5 then
                plus = -1 * plus
            end

            self.data[index][j] = limit( 0,1, self.data[index][j] + plus )
        end
        self.data[index][5] = 1 -- the upper limit is always 1

        local tmp = self.data[index][6] -- The fitness value should not be ordered.
        self.data[index][6] = nil
        table.sort( self.data[index] ) -- The values can get mixed and so they're resorted.
        self.data[index][6] = tmp
    end,
}

main()

Should it violate any rule let me know and i'll fix it asap.

robotik

Posted 2014-07-25T15:39:06.563

Reputation: 21

Update to new version 3:

  • changed the way the fitness value is calculateed
  • fixed a problem with Lua's RNG
  • initial generation of data is now uniform

I hope in the next tournament it'll fare better than the last one! :D – robotik – 2014-08-02T19:34:18.937

1cool. will take a note to update it – Eoin Campbell – 2014-08-03T10:05:29.933

2

qeylIs-bortaS - var'aq

I do believe this is the only one in Klingon...

Chooses a random number and 'translates' the number into R, P, S or L. The program is biased against Spock, because Klingons hate Vulcans (I think...).

Run the following shell script (it is in bash, so you might need to translate it to batch):

wget http://www.reocities.com/connorbd/varaq/varaq-current.zip
unzip varaq-current.zip
mv qeylIs-bortaS.vq ./varaq-current
rename varaq-kling varaq-kling.pl

To run the program:

perl varaq-kling.pl < qeylIs-bortaS.vq [arg1] [arg2] | findstr /R /l "^[RPSL]$"

Note: The findstr at the end is to get rid of

var'aq Reference Edition -- Klingon v0.5
(c)2000 Brian Connors, Chris Pressey, and Mark Shoulson

at the start of the program.

Program

(* qeylIs-bortaS.vq *)

6 mIS poD ~ SIq cher (* Generate random number from 0-6 *)

SIq 0 rap'a' { "R" cha' } HIja'chugh
SIq 1 rap'a' { "P" cha' } HIja'chugh
SIq 2 rap'a' { "P" cha' } HIja'chugh
SIq 3 rap'a' { "S" cha' } HIja'chugh
SIq 4 rap'a' { "L" cha' } HIja'chugh
SIq 5 rap'a' { "L" cha' } HIja'chugh
SIq 6 rap'a' { "L" cha' } HIja'chugh

Here's the English version - Kahless' Revenge:

This is just a translation, use qeylIS-bortaS for the tourney.

Run

perl varaq-engl < kahless-revenge.vq [arg1] [arg2] | findstr /R /l "^[RPSL]$"

Code:

6 rand clip ~ index set (* Generate random number from 0-6 *)

index 0 eq? { "R" disp } ifyes
index 1 eq? { "P" disp } ifyes
index 2 eq? { "P" disp } ifyes
index 3 eq? { "S" disp } ifyes
index 4 eq? { "L" disp } ifyes
index 5 eq? { "L" disp } ifyes
index 6 eq? { "L" disp } ifyes

Beta Decay

Posted 2014-07-25T15:39:06.563

Reputation: 21 478

2

The RandomSpockBot, ruby

Simply picks a random move from an array, but it's always the best possible move.

puts ['V'].sample

theonlygusti

Posted 2014-07-25T15:39:06.563

Reputation: 1 221

1

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]);
    }
}

Guillaume Pagès

Posted 2014-07-25T15:39:06.563

Reputation: 31

1

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.

Stretch Maniac

Posted 2014-07-25T15:39:06.563

Reputation: 3 971

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" – Eoin Campbell – 2014-07-30T13:13:28.687

1

Java - DogeBot2.0

This is nearly identical to my last submission but it's a little smarter this time. This one only looks at the oppenents last five hands, instead all of them. Also, instead of just printing P when there is less than five hands played, it will beat whatever the last one was.

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

        String possibleHands = "RPSLV";

        if (args.length < 2) {
            System.out.println("P");
            return;
        }

        String yourHistory = args[1];  

        if (yourHistory.length() < 5) {
            char lastHistoryHand = yourHistory.charAt(yourHistory.length() - 1);
            int i = possibleHands.indexOf(lastHistoryHand);

            char newHand = possibleHands.charAt((i + 2) % 5);
            System.out.println(newHand == 'S' ? "P" : newHand);
            return;
        }    

        yourHistory = yourHistory.substring(yourHistory.length() - 5);

        for (int i = 0; i < 5; i++) {
            String searchString = "" + possibleHands.charAt(i) + possibleHands.charAt(i);

            if (yourHistory.contains(searchString)) {

                char newHand = possibleHands.charAt((i + 2) % 5);
                System.out.println(newHand == 'S' ? "P" : newHand);

                return;
            } else continue;

        }

        char lastHistoryHand = yourHistory.charAt(yourHistory.length() - 1);
        int i = possibleHands.indexOf(lastHistoryHand);

        char newHand = possibleHands.charAt((i + 2) % 5);
        System.out.println(newHand == 'S' ? "P" : newHand);

        return;
    }
}

Milo

Posted 2014-07-25T15:39:06.563

Reputation: 3 082

1

Robotic Oboe Bot Oboe Tuner - Python 2

Goes through each letter its opponent has submitted and looks for any recurring strings in both it and its opponent's history before that point.

Every possible contiguous string before the letter it's checking will be noted in a dictionary along with the distance that string appeared from the letter being checked. This dictionary persists through each letter that is checked and each time the same substring appears the same distance from the letter being checked a counter for that substring in the dictionary is incremented. This process is done separately for both the opponent's history + the opponent's chosen letter and this bot's history + the opponent's chosen letter.

Once each pattern dictionary has been assembled; the bot goes through each letter that was used by the opponent and compares the patterns to the current history. If there is a match with the correct distance from the end then the letter being tested gets a point for every occurrence that substring had had. If the pattern is repeating then the letter will get points separately for each time it had appeared before that as each distance + string combo is considered a distinct dictionary entry.

Once the most probable patternistic next move has been determined - a random countering move is chosen for that move.

In the case where there is a tie; one of the letters in the tie is picked at random. When there are no patterns to work off (before the opponent has repeated any letters) - the bot picks randomly.

The cool thing about this implementation is that because the letters get a flat score for each pattern detected; more recent characters tend to have more weight than older characters as they will have more patterns preceding them and longer patterns will have exponentially more weight as the sub-patterns within them will also be counted as separate patterns. This bot should even deal with bots that periodically alternate strategies once it figures out the pattern.

import sys
import random

counters = {"R": "VP", "P": "SL", "S": "VR", "L": "RS", "V": "LP"}
valid_letters = "RPSLV"

# Create a dictionary of all possible patterns of how the cause history could have affected the effect history's next letter
def generate_pattern_dict(cause_history, effect_history):
    dictionary = {}

    # Run through all the rounds so far
    for round in xrange(len(cause_history)):

        history = cause_history[:round][::-1]
        letter = effect_history[round]

        if not letter in dictionary:
            dictionary[letter] = {}

        # Loop through every size of a string that could fit in the history
        for hs_len in range(1, len(history) + 1):

            # Loop through every position a string of that size could be in
            for hs_pos in range(len(history) - hs_len + 1):

                # Add this occurance to the dictionary; noting how long before the letter it occured and what it was
                history_string = history[hs_pos:hs_pos + hs_len]
                dist = hs_pos
                try:
                    dictionary[letter][(dist, history_string)] += 1
                except KeyError:
                    dictionary[letter][(dist, history_string)] = 1
    return dictionary

# Given a pattern dictionary; predict the next letter based on a history
def get_probabilities(patterns, history):
    probs = {}
    history = history[::-1]

    # Look at all the letters that have been used
    for letter in patterns:
        probs[letter] = 0

        # Look at all the known string patterns to have preceded this string and mark this letter as more likely the more strings that point to it
        for key, occurrences in patterns[letter].iteritems():
            dist, history_string = key

            if history[dist: dist + len(history_string)] == history_string:
                probs[letter] += occurrences

    return probs

def get_most_probable(dictionary):
    highest_prob = 0
    hightst_letters = ""

    for letter, prob in dictionary.iteritems():
        if prob >= highest_prob:
            if prob > highest_prob:
                hightst_letters = letter
                highest_prob = prob
            else:
                hightst_letters += letter

    if len(hightst_letters) == 0:
        hightst_letters = valid_letters

    return random.choice(hightst_letters), highest_prob

def get_histories():
    try:
        return (sys.argv[1], sys.argv[2])
    except IndexError:
        return ("", "")

def counter(letter):
    return random.choice(counters[letter])

def get_hand(my_history, opponent_history):

    # Look at the affect the opponent's moves have on what they choose next
    pattern_dict = generate_pattern_dict(opponent_history, opponent_history)
    pattern_probs = get_probabilities(pattern_dict, opponent_history)
    pattern_letter, pattern_prob = get_most_probable(pattern_probs)

    # Look at what affect my moves have on what the opponent chooses next
    prediction_dict = generate_pattern_dict(my_history, opponent_history)
    prediction_probs = get_probabilities(prediction_dict, my_history)
    prediction_letter, prediction_prob = get_most_probable(prediction_probs)

    if pattern_prob > prediction_prob:
        opponent_letter = pattern_letter
    elif pattern_prob < prediction_prob:
        opponent_letter = prediction_letter
    else:
        opponent_letter = random.choice((pattern_letter, prediction_letter))

    return counter(opponent_letter)


print get_hand(*get_histories())

Run with:

    python robotic_oboe_bot_oboe_tuner.py <args>

Docopoper

Posted 2014-07-25T15:39:06.563

Reputation: 51

I assume that's you SF ;-) damn long bot name broke my grid formatter :-) – Eoin Campbell – 2014-07-29T18:28:40.910

1

Excitingish Bot (Java)

Evolved Boring Bot to take a risk and assume everyone else will try to outsmart him by considering his last move.

public class ExcitingishBot
{
    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("R");
            return;
        }

        char[] oppPreviousPlays = args[1].toCharArray();
        char[] myPreviousPlays = args[0].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++;
            }
        }

        if (myPreviousPlays.length()>2){
        for (int j=1; j<3; j++) {
            switch(myPreviousPlays[myPreviousPlays.length()-j]){
                case 'R': Paper+=5; Spock+=5; break;
                case 'P': Scissors+=5; Lizard+=5; break;
                case 'S': Rock+=5; Spock+=5; break;
                case 'L': Scissors+=5; Rock+=5; break;
                case 'V': Paper+=5; Lizard+=5;
              }
        }}

        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;
        }
    }
}

kaine

Posted 2014-07-25T15:39:06.563

Reputation: 536

1

Botzinga - Java

BotName: Botzinga
Compile: Save as Botzinga.java and compile as usual
Run:     Botzinga [Arg1] [Arg2]    

The code:

public class Botzinga {

private static int curTurn = 0;
private static char[] opHand;
private static char[] myHand;

public static void main(String[] args) {

    if(args.length != 0) {
        int curTurn = args[0].length();
        opHand = args[1].toCharArray();
        myHand = args[0].toCharArray();
    }
    char c = ' ';

    if(curTurn >= 5) {
        if(opHand[curTurn-1] == opHand[curTurn-2] && opHand[curTurn-1] == opHand[curTurn-3])
            c = beater(opHand[curTurn - 1]);
        else {
            if (beats(myHand[curTurn - 1], opHand[curTurn - 1]))
                c = beater(myHand[curTurn - 1]);
            else
                c = beater(opHand[curTurn - 1]);
        }

    }
    else
        c = playRandom();
    System.out.print(c);

}


public static char playRandom() {
    char c = ' ';
    switch((int)(Math.random()*5)) {
        case 0: c = 'R'; break;
        case 1: c = 'P'; break;
        case 2: c = 'S'; break;
        case 3: c = 'L'; break;
        case 4: c = 'V'; break;
    }
    return c;
}

private static char beater(char a) {
    int Rock=0;
    int Paper=0;
    int Scissors=0;
    int Lizard=0;
    int Spock=0;

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

    switch (a) {
        case 'R': return Paper < Spock ? 'P' : 'V';
        case 'P': return Scissors < Lizard ? 'S' : 'L';
        case 'S': return Rock < Spock ? 'R' : 'V';
        case 'V': return Paper < Lizard ? 'P' : 'L';
        case 'L': return Rock < Scissors ? 'R' : 'S';
        default: return ' ';
    }

}

private static boolean beats(char a, char b) {
    boolean win = false;
    switch (a) {
        case 'R':
            win = b == 'S' || b == 'L';
            break;
        case 'P':
            win = b == 'R' || b == 'V';
            break;
        case 'S':
            win = b == 'P' || b == 'L';
            break;
        case 'V':
            win = b == 'S' || b == 'R';
            break;
        case 'L':
            win = b == 'V' || b == 'P';
            break;
    }
    return win;
}
}

Luis Mars

Posted 2014-07-25T15:39:06.563

Reputation: 11

1

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

john Smith

Posted 2014-07-25T15:39:06.563

Reputation: 111

definitely have you in this time :-) – Eoin Campbell – 2014-08-01T16:29:59.957

yesss baby i´m excited – john Smith – 2014-08-01T18:48:27.113

1

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

Thaylon

Posted 2014-07-25T15:39:06.563

Reputation: 1 324

1

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

Thaylon

Posted 2014-07-25T15:39:06.563

Reputation: 1 324

1

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.

Carlos Martinez

Posted 2014-07-25T15:39:06.563

Reputation: 11

1

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;
    }

}

Josef E.

Posted 2014-07-25T15:39:06.563

Reputation: 131

1

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.

Docopoper

Posted 2014-07-25T15:39:06.563

Reputation: 51

Renamed OboeBeater – Eoin Campbell – 2014-07-30T19:55:38.410

This is the only bot in the field so far that consistently beats my bot Pony. :) – Emil – 2014-07-31T12:09:29.983

Hahaha. xD Well you could easily special case it. – Docopoper – 2014-07-31T12:50:10.753

@Pony any idea how it would perform against a bot which was making it's decisions against something like the Linus Sequence ? – Eoin Campbell – 2014-08-01T08:18:28.160

1

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]);
        }
    }
}

ja72

Posted 2014-07-25T15:39:06.563

Reputation: 437

this bot is broken. args[0] won't exist on the first iteration. – Eoin Campbell – 2014-07-30T21:12:47.403

Good catch. I fixed it. – ja72 – 2014-07-30T22:19:28.320

1

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

Docopoper

Posted 2014-07-25T15:39:06.563

Reputation: 51

renamed OboeOboeBeater :-) – Eoin Campbell – 2014-07-30T19:50:56.950

Hahaha. Though it doesn't beat RoboticOboeBotOboeTuner, it beats TheBotYouCouldEasilySpecialCaseAgainst. – Docopoper – 2014-07-30T21:40:23.410

1

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.

histocrat

Posted 2014-07-25T15:39:06.563

Reputation: 20 600

yup. included in next run. – Eoin Campbell – 2014-07-31T17:35:17.110

1

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

Stranjyr

Posted 2014-07-25T15:39:06.563

Reputation: 111

python 2 or 3 ? – Eoin Campbell – 2014-07-31T16:03:43.543

python 3 should do it. – Stranjyr – 2014-07-31T17:32:42.480

1getting 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. – Eoin Campbell – 2014-07-31T17:34:11.687

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 – Eoin Campbell – 2014-07-31T17:34:46.510

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. :/ – Stranjyr – 2014-07-31T17:39:40.890

haha. yeah... funnily enough my wife is expecting our first kid this week... so this is keeping me occupied/distracted :-) I may go AWOL at any minute tho :-) – Eoin Campbell – 2014-07-31T17:55:40.443

also that's working now & included :-) – Eoin Campbell – 2014-07-31T17:57:40.350

1

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)

ovenror

Posted 2014-07-25T15:39:06.563

Reputation: 111

pytho2 or 3 or does it matter ? – Eoin Campbell – 2014-08-01T13:34:01.013

Yes, it needs Python2. – ovenror – 2014-08-01T14:14:50.187

2Oh and BTW: Thanks for your awesome game master work here :) – ovenror – 2014-08-01T14:20:16.293

1

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;
    }
}

Malachi

Posted 2014-07-25T15:39:06.563

Reputation: 240

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." – Martin Ender – 2014-08-01T22:23:59.690

1Thank you @MartinBüttner I will edit it a little and repost! – Malachi – 2014-08-01T22:25:41.123

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 – Eoin Campbell – 2014-08-01T22:26:06.077

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 – Eoin Campbell – 2014-08-04T21:17:38.200

@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. – Malachi – 2014-08-04T22:49:12.220

looks like I am missing some brackets and other things... I don't think I finished pulling pieces and stuff into Notepad++ when I copy pasta'd it in here, sorry – Malachi – 2014-08-04T22:50:32.447

no problem.... :-) learning == good – Eoin Campbell – 2014-08-05T09:44:59.877

1

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!

Ian

Posted 2014-07-25T15:39:06.563

Reputation: 11

1

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

Beta Decay

Posted 2014-07-25T15:39:06.563

Reputation: 21 478

1

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=

Kyle McCormick

Posted 2014-07-25T15:39:06.563

Reputation: 3 651

1

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.

ASCIIThenANSI

Posted 2014-07-25T15:39:06.563

Reputation: 1 935

0

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

Masclins

Posted 2014-07-25T15:39:06.563

Reputation: 914