# 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? – histocrat Jul 25 '14 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. – fluffy Jul 25 '14 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. – Kyle Kanos Jul 26 '14 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 – Eoin Campbell Jul 28 '14 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 '14 at 7:07

# 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). – Martin Ender Jul 28 '14 at 11:27
• @MartinButtner Thanks – Eoin Campbell Jul 28 '14 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 – Eoin Campbell Jul 28 '14 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).

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.... – Stranjyr Jul 29 '14 at 14:36
• That's Brilliant :-) – Eoin Campbell Jul 30 '14 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>' – Eoin Campbell Jul 28 '14 at 18:23
• @EoinCampbell Sorry, should be fixed now ... hopefully ... – Thaylon Jul 28 '14 at 18:53

## Edward Scissorhands

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. – Martin Ender Jul 29 '14 at 11:42
• How about Sam Neill/Alan Grant? :P ... There are probably better options though. – Martin Ender Jul 29 '14 at 11:49
• Yeah. Daenerys is more dragon than lizard. Kaiju maybe ;-) – Eoin Campbell Jul 29 '14 at 12:51
• @EoinCampbell Carry on complaining and I'll make you run it as DaenerysTargaryenStormbornKhaleesiQueenOfTheAndalsAndTheFirstMenBreakerOfChains.... :P – Dr R Dizzle Jul 30 '14 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 :-) – Eoin Campbell Jul 30 '14 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 :-) – Eoin Campbell Jul 28 '14 at 1:27
• What does (or did) this do? – justhalf Jul 30 '14 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. – histocrat Jul 30 '14 at 13:19
• cool... noted & withdrawn – Eoin Campbell Jul 31 '14 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. – Eoin Campbell Aug 1 '14 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 (!). – Roy van Rijn Jan 7 '15 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" :-) – Eoin Campbell Jul 27 '14 at 19:30 # 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  • Some 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 Jul 25 '14 at 21:44
• 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 Jul 25 '14 at 22:37
• 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 Jul 25 '14 at 23:07
$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)  • 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 Jul 26 '14 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 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] ## 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 '14 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 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 # 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] # 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 • +1 for the name, but I have no idea what it does. – Martin Ender Jul 28 '14 at 8:23 • @MartinBüttner Explanation is up now! :) – Οurous Jul 28 '14 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 () (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! • 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 Jul 29 '14 at 5:48 • Thanks for the submission. will have a look at adding lisp support to the orchestration engine this evening. :-) – Eoin Campbell Jul 29 '14 at 7:52 • @EoinCampbell I updated the compile command to be a one liner if that helps you. – NonFunctional User29916 Jul 30 '14 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. – Eoin Campbell Jul 31 '14 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 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. • 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 Aug 2 '14 at 19:34 • cool. will take a note to update it – Eoin Campbell Aug 3 '14 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 :-) – Eoin Campbell Jul 29 '14 at 18:28