# 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

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.

• 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

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

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

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


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'  # 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])  # 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; } }  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']; } } ?>  • Welcome 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). Jul 28, 2014 at 11:27 • @MartinButtner Thanks Jul 28, 2014 at 18:32 • Hey 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 Jul 28, 2014 at 18:33 ## 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). • So, I checked the logs, and your suicidal Lemmingbot beat my toddleProof bot 100 - 0 because our patterns exactly coincided. Ouch.... Jul 29, 2014 at 14:36 • That's Brilliant :-) Jul 30, 2014 at 12:37 # 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  • 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>' Jul 28, 2014 at 18:23 • @EoinCampbell Sorry, should be fixed now ... hopefully ... Jul 28, 2014 at 18:53 ## 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  • :D I almost would have posted exactly that in the beginning of the challenge, but then I went for Vulcan instead. Jul 29, 2014 at 11:42 • How about Sam Neill/Alan Grant? :P ... There are probably better options though. Jul 29, 2014 at 11:49 • Yeah. Daenerys is more dragon than lizard. Kaiju maybe ;-) Jul 29, 2014 at 12:51 • @EoinCampbell Carry on complaining and I'll make you run it as DaenerysTargaryenStormbornKhaleesiQueenOfTheAndalsAndTheFirstMenBreakerOfChains.... :P Jul 30, 2014 at 11:32 • I'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 :-) Jul 30, 2014 at 12:39 # 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. • :-) 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 :-) Jul 28, 2014 at 1:27 • What does (or did) this do? Jul 30, 2014 at 9:34 • It 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. Jul 30, 2014 at 13:19 • cool... noted & withdrawn Jul 31, 2014 at 17:05 # 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 • harsh dude :-) I'll update your source later and include it in the next round. Aug 1, 2014 at 13:32 • I'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 (!). Jan 7, 2015 at 16:01 # 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

• Renamed as "MAWBRBot" :-) Jul 27, 2014 at 19:30

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

• Sheldor says "Who's this trouble maker"... leave it with me... I'll see if I can coax him round ;0) Jul 25, 2014 at 19:54
• I'll set it up with node later. cheers Jul 28, 2014 at 13:25

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


## 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'};
Console.WriteLine(a == default(char) ? 'S' : a);
}
catch (Exception)
{
Console.WriteLine("R");
}
}
}
}


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

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

• Does this only return rock?
– user8777
Jul 28, 2014 at 1:59
• Bart wises up eventually. Jul 28, 2014 at 3:39

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

• you're gonna burn out my machine dude :-) Jul 28, 2014 at 1:35
• Heh, sorry. Don't worry, I'm done. Jul 28, 2014 at 1:40
• kidding man. keep em coming. you're keeping my on my toes :-) Jul 28, 2014 at 1:49

# 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
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']
Hash[paired_options_and_probs]
end

File.open(PRIORITIES_FILE_PATH, 'r') do |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)

• 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>' Jul 26, 2014 at 0:13

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

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

• 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
Jul 29, 2014 at 3:21

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

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

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

• +1 for the name, but I have no idea what it does. Jul 28, 2014 at 8:23
• @MartinBüttner Explanation is up now! :) Jul 28, 2014 at 8:44

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

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

• 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! Jul 29, 2014 at 5:48
• Thanks for the submission. will have a look at adding lisp support to the orchestration engine this evening. :-) Jul 29, 2014 at 7:52
• @EoinCampbell I updated the compile command to be a one liner if that helps you. Jul 30, 2014 at 5:00
• yep. that worked fine once I figured out I needed to set a %SBCL_HOME% env variable. It's incl. in the current run. Jul 31, 2014 at 20:29

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


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

• 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 Aug 2, 2014 at 19:34
• cool. will take a note to update it Aug 3, 2014 at 10:05

# 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


# The RandomSpockBot, ruby

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

puts ['V'].sample


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


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


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


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

• I assume that's you SF ;-) damn long bot name broke my grid formatter :-) Jul 29, 2014 at 18:28