# 4-Man Standoff

## Description

You've somehow found yourself into a four-way standoff. A loaded gun rests in your hands, and some grenades are hooked on your belt.

The objective is to have the most health at the end of a standoff. A standoff is over when at most one person has a positive amount of health.

Each player has 5 health, and dies when their health drops to/below 0. The turn a player dies is the last turn that player can take damage.

If there is a live player at the end of a standoff, that player wins. Otherwise, the player with the least negative health wins.

## Actions

• Shoot: Take a shot at someone.

• 2 damage if shooting a live enemy
• 0 damage if shooting a dead enemy
• health_at_start_of_turn+2 damage if shooting yourself. (Note that this will leave you with at MOST -2 health.)
• If one enemy shoots at you on the same turn you shoot yourself, you will end the standoff with -4 health (you still take damage from other players the turn you kill yourself).
• Your action the following turn will be ignored (and assumed to be Nothing).
• Dodge: Try to dodge a single opponent's shot.

• You only have three turns to throw it, before you get blown up (6 damage to yourself, 3 damage to all live enemies)
• Dying with an unthrown grenade is equivalent to not throwing the grenade for three turns.
• Throw: Chuck the grenade towards someone and hope for the best.

• Target receives 8 damage if alive
• Everyone else (including yourself) receives 3 damage if alive
• Nothing: Stand idly for a turn and watch everyone die.

## Input

Your program will be passed the following information:

• The health of each player
• A list of actions taken by that player since the start of the standoff Below is the format for the information passed per player:

[Health],[Action 1],[Action 2],[Action 3],...


Actions will be given in the format specified in the Output section.

You will recieve 4 such strings, separated by a space, and passed as a single argument. The order of these strings is:

[Player Info] [Opponent 1 Info] [Opponent 2 Info] [Opponent 3 Info]


The strings are passed as the second argument. The first argument contains an integer that uniquely identifies the standoff being enacted. Standoffs between the same set of players are guaranteed not to be simultaneous. However, multiple standoffs will occur at the same time.

For example:

$./Player.bash 5 "3,S2,N 5,P,N 3,S0,N -2,S3,N"  Currently, the player and the second opponent have 3 health, the first opponent has 5 health, and the third opponent has -2 health and is dead. On the first turn: • Player 1 shot enemy 2 • Enemy 1 prepared a grenade • Enemy 2 shot player • Enemy 3 shot himself On the second turn: • All players did nothing. (Player and enemy 2 can not do anything since they shot on the previous turn. Enemy 3 is dead: he will do Nothing for the rest of the standoff.) The second argument at the start of a standoff is: 5 5 5 5. ## Output A command should be outputted in the below listed format. An invalid output is interpreted as 'Nothing'. A command requiring a target should be followed by an integer (0-3, with 0 representing the player, and 1-3 representing enemies 1-3). • S[target]: Shoots [target]. • D[target]: Tries to dodge [target]. • P: Prepare a grenade. • T[target]: Throw the grenade at [target]. • N: Do nothing. A command that needs a target, but is fed a target not between 0 and 3 or not fed a target entirely will be assumed to target 0 (the player). ## Scoring At the end of each standoff, players receive a score calculated by the following formula: 35 + health at end of standoff  In the case that a player ends a standoff with negative health, they will receive a score below 35. The following points are also rewarded as a bonus: • Most health: +4 points • Second most health: +2 points • Third most health: +1 point. In case of a tie, the lower bonus is granted (if two people tie with most health, both are granted +2; if there are 3 people with the most health, +1, and if everyone ends equally, +0). The final score is determined by calculating the mean of all individual scores. ## Rules/Details • Order of events within a turn are as follows: • All players do their actions. • Players who have 0 or less health die. • Unthrown grenades that need to explode, will explode (players who just died are still hurt, as this is still the turn they died). • No collaboration between entries. • Three* standoffs will occur between each set of 4 players. (Order of players may vary with each standoff). • Entries consuming excessive amounts of memory of disk space will be disqualified. • Reading from or modifying files other than your entry's will disqualify your entry. • A truck, driven by a drunkard, will run over all living players after the 50th turn, if the standoff is not yet over at the end of the 50th turn. • This truck deals 20 damage to all live players. • Standoffs happen quickly. Programs are cut off after 1 second. • Your program will be called every turn, even after you have died. • You may read or write files to your directory only (if your entry is named JohnDoe, you may save files in the directory players/JohnDoe/); however, this will NOT be the current directory while your script is running. • The standoffs will take place on a machine running Arch Linux (Release 2014.08.01). The controller is available on GitHub. Please include the following in your post: • A name for your bot • A shell command to run the bot (ex. java Doe.java) Input will be passed through the command line as a single argument (java Doe.java 5 "-2,S0 -2,S1 -2,S2 5,N") • Code of your bot • How the bot should be compiled (if applicable) • Language (and version if applicable, especially for python) *Controller is taking way too long for six. ## Scoreboard  Observer 43.280570409982 MuhammadAli 43.134861217214 Osama 43.031983702572 LateBoomer 42.560275019099 SimpleShooter 42.412885154062 LessSimpleShooter 42.3772 Neo 42.3738 Scared 42.3678 Richochet 42.3263 Equivocator 42.2833 TwentyFourthsAndAHalfCentury 42.2640 Darwin 42.1584 HanSolo 42.1025 Coward 42.0458 ManipulativeBastard 41.8948 Sadist 41.7232 Aggressor 41.7058 CourageTheDog 41.5629 Grenadier 40.9889 Bomberman 40.8840 Spock 40.8713 Sniper 40.6346 DONTNUKEMEBRO 39.8151 PriorityTargets 39.6126 Hippolyta 39.2480 EmoCowboy 39.2069 Zaenille 39.1971 AntiGrenadier 39.1919 PoliticallyCorrectGunman 39.1689 InputAnalyzer 39.1517 Rule0Bot 39.1000 BiasedOne 39.0664 Pacifist 39.0481 StraightShooter 39.0292 Ninja 38.7801 MAD 38.2543 Monkey 37.7089 Label1Goto1 36.2131 Generated: 2014/08/22 03:56:13.470264860 UTC  Logs: on GitHub • Do you have exactly one grenade, or do you have many? Can you have multiple grenades being prepared at a time? – isaacg Aug 9 '14 at 23:52 • @Bob Pretty sure EmoWolf got added to Standard Loopholes which are no longer funny. Though a suicidal entry might not actually do so terribly. – es1024 Aug 11 '14 at 1:48 • Lesson to everyone : Don't drink and drive. – Zaenille Aug 11 '14 at 1:56 • @es1024 Where suicide is actually a viable strategy, an EmoWolf-type submission really should be allowed. Especially when the available actions explicitly include suiciding! Not so much a "loophole" now, is it? And not really an unfair advantage, which most of those loopholes really are. But that's just my opinion. – Bob Aug 11 '14 at 2:20 • Based on running the controller a couple times it seems pretty noisy. If this contest ever gets closed you should probably up the number of runs to smooth it out a bit. – Davis Yoshida Aug 12 '14 at 4:47 ## 40 Answers ## Late Boomer Ruby There are 3 types of players right now. Ones who never use grenades, ones who pull first turn, and one idiot who can do anything (that actually sounds inspiring). This class attempts to change that slightly. He pulls the 4th turn should more than 1 opponent be alive, he has no less than 3 health, and noone else has pulled. While I've included a python entry, these are clearly not colluding as he nukes this guy turn 2 ususally. def health(guy) return guy.split(",")[1].to_i end def bomb(past) return past.count("P")-past.count("T")>0 end def maxhealth(players) target=players[0] for dude in players if health(dude)>health(target) target=dude end end return target end def reloading(past) return past[past.length-2]=="S" end input=ARGV[1] them = input.split(" ") me = "0,"+them.delete_at(0) turn = me.split(",").length-1 x=0 y=0 while y<them.length do x +=1 them[y]=x.to_s+","+them[y] if health(them[y])<1 them.delete_at(y) y -=1 end y +=1 end if turn==1 print "D1" elsif bomb(me) print "T"+ maxhealth(them)[0][0].to_s elsif turn>2 and not(bomb(input)) and them.length>1 and health(me)>2 and not(reloading(me)) print "P" elsif bomb(input) print "S"+maxhealth(them)[0][0].to_s else print "S"+them[0][0] end  # Muhammad Ali - Java Floats like a butterfly, stings like a bee; will normally dodge the player who has taken most shots and is ready to shoot. Shoots the healthiest looking player whenever no one can move or if it's one on one and the other player has a grenade prepared. Will not hesitate to take his own life should it be necessary. import java.util.ArrayList; import java.util.Random; public class MuhammadAli { public static void main(String[] args) { if(args.length < 2) { return; // nothing to do here } String[] players = args[1].split(" "); if(players.length < 4 || !isAlive(players[0])) { return; // nothing to do here } Random rand = new Random(); int turn = players[0].split(",").length; int highestHealth = -1; int healthiestPlayer = -1; int livingPlayers = 0; for(int i=1; i<4; i++) // who is doing too well for their own good? { int health = getHealth(players[i]); if(health>=0) { livingPlayers++; } if(health>highestHealth || (health==highestHealth && rand.nextDouble()>0.5)) { highestHealth = health; healthiestPlayer = i; } } if(livingPlayers==1 && ((turn==49 && highestHealth<2) || grenadeExplodes(players[healthiestPlayer])>-1)) { System.out.print("S" + Integer.toString(healthiestPlayer).charAt(0)); // you had it coming dude } if(turn!=49) { int mostShots = -1; int triggerHappyPlayer = -1; for(int i=1; i<4; i++) // who's the most trigger happy player? { int shots = shotsTaken(players[i]); if(canMove(players[i]) && (shots>=mostShots)) { if(shots>mostShots || ((grenadeExplodes(players[i])==-1 || rand.nextDouble()>0.5) && grenadeExplodes(players[triggerHappyPlayer])==-1)) // if you're holding a grenade, you'll have other things on your mind than shooting. mostShots = shots; triggerHappyPlayer = i; } } if(mostShots>0 || turn < 2) { System.out.print("D" + Integer.toString(triggerHappyPlayer)); // you didn't get your hopes up, did you? return; } } if(highestHealth>2 && turn==49) { System.out.print("S0"); // Goodbye, cruel world } else { System.out.print("S" + Integer.toString(healthiestPlayer).charAt(0)); // seems like I can take a free shot at someone } } public static boolean isAlive(String player) { return player.charAt(0)!='-'; } public static boolean canMove(String player) { return isAlive(player) && (player.length() == 1 || player.charAt(player.length()-2)!='S'); } public static int grenadeExplodes(String player) { String[] actions = player.split(","); if(actions.length>3 && actions[actions.length - 3].charAt(0)=='P' && actions[actions.length - 2].charAt(0)=='T' && actions[actions.length - 1].charAt(0)=='P') { return 0; } else if(actions.length>2 && actions[actions.length - 2].charAt(0)=='P' && actions[actions.length - 1].charAt(0)=='T') { return 1; } else if(actions.length>1 && actions[actions.length - 1].charAt(0)=='P') { return 2; } else { return -1; } } public static int shotsTaken(String player) { String[] actions = player.split(","); int shots = 0; for(int i=0; i<actions.length; i++) { if(actions[i].charAt(0)=='S') { shots ++; } } return shots; } public static int getHealth(String player) { return Integer.parseInt(player.split(",")[0]); } }  I'm hoping this will do quite well, since he is pretty smart about dodging and only starts agression when it really seems to play in his advantage. • Outputs nothing when java MuhammadAli 32 "5 5 5 5" is called. – es1024 Aug 18 '14 at 2:22 • Whoops, forgot an exclamationmark. – overactor Aug 18 '14 at 5:50 • Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.charAt(String.java:658) at MuhammadAli.canMove(MuhammadAli.java:82) at MuhammadAli.main(MuhammadAli.java:49) only for the input 5 5 5 5 – es1024 Aug 18 '14 at 23:01 • @es1024 damn, I need to think more about what I code, should be fixed now. – overactor Aug 19 '14 at 4:35 # DON'T NUKE ME BRO! - Lua Doesn't like it when people use their grenades. Is a hypocrite when more than one person is shooting at him. --Don't Nuke Me Bro math.randomseed(os.time()) info=arg[2] function tablelength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end function parseEnemies(s) -- Makes sense of the input Data local e={} local pStatus={{},{},{},{}} for w in string.gmatch(s, "%S+") do table.insert(e,w) end for p = 1, 4 do for a in string.gmatch(e[p], "%P+") do table.insert(pStatus[p], a) end end for i = 1 , 4 do pStatus[i][1]=tonumber(pStatus[i][1]) end return pStatus end function analyseEnemies(ps) moveList={{},{}} sFlag = 0 if tablelength(ps[1])>=35 then for i = 2, 4 do if ps[i][1]>0 then table.insert(moveList[1], "S"..i-1) table.insert(moveList[2], 100) end end elseif tablelength(ps[1])>=42 then table.insert(moveList[1], "P") table.insert(moveList[2], 100) elseif tablelength(ps[1])>=48 then table.insert(moveList[1], "S0") table.insert(moveList[2], 100) else for i=2,4 do pHealth = ps[i][1] lan=tablelength(ps[i]) lAction=ps[i][lan] llAction=ps[i][lan-1] if pHealth <= 0 then else if lAction=="P" then -- Doesn't Like it when people goes nuclear table.insert(moveList[1], tostring("S"..i-1)) h=8-pHealth table.insert(moveList[2], h) end if llAction=="S0" then -- takes notice when someone shoots at him sFlag = sFlag +1 if pHealt == 2 or 1 then table.insert(moveList[1],tostring("S".. i-1)) table.insert(moveList[2], 2) else table.insert(moveList[1],tostring("D"..i-1)) table.insert(moveList[2], 3) end end end end end nmla = tablelength(ps[1]) if nmla == 2 and ps[1][nmla] == "P" then -- gotta get rid of this grenade tH= 0 target = 1 for i = 2, 4 do if ps[i][1]>=tH then target= i-1 end end table.insert(moveList[1],tostring("T"..target)) table.insert(moveList[2], 6) end if nmla == 3 and ps[1][nmla-1] == "P" then -- Makes sure he throws the damn grenade tH= 0 target = 1 for i = 2, 4 do if ps[i][1]>=tH then target= i-1 end end table.insert(moveList[1],tostring("T"..target)) table.insert(moveList[2], 20) end if sFlag >=2 then -- if 2 or more people shoot at him during a round, he goes nuclear. table.insert(moveList[1],tostring("P")) table.insert(moveList[2], 9) end r=math.random(3) table.insert(moveList[1], "D"..r) -- random ducking if all else fails table.insert(moveList[2], 1) lan=tablelength(ps[1]) lAction=ps[1][lan] if lAction =="P" then for i = 2, 4 do if ps[i][1]>0 then table.insert(moveList[1], "T"..i-1) table.insert(moveList[2], 100) end end end return moveList end function makeMove(mList) -- picks the highest rated move m = "" nml = tablelength(mList[2]) c = 0 m = 1 for i = 1, nml do if mList[2][i]>= c then c = mList[2][i] m = mList[1][i] end end print(m) end makeMove(analyseEnemies(parseEnemies(info)))  run it like lua DONTNUKEMEBRO.lua "input string"  • lua DONTNUKEMEBRO.lua 43 "5 5 5 5" gives: lua: DONTNUKEMEBRO.lua:28: bad argument #1 to gmatch (string expect, got nil), status traceback: [C]: in function 'gmatch' DONTNUKEMEBRO.lua:28: in function 'parseEnemies' DONTNUKEMEBRO.lua:119: in main chunk [C]: in ? – es1024 Aug 10 '14 at 5:07 • @es1024 Yep, forgot about the unique stand off number. Sorry about that. It should be okay to go now. – AndoDaan Aug 10 '14 at 7:17 • lua: DONTNUKEMEBRO.lua:43: attempt to call field 'getn' (a nil value) stack traceback: DONTNUKEMEBRO.lua:43: in function 'analyseEnemies' DONTNUKEMEBRO.lua:119: in main chunk [C]: ? – es1024 Aug 10 '14 at 7:22 • @es1024 Okay, I think I got it now. I was using an out of date function. Sorry for the hassle. – AndoDaan Aug 10 '14 at 8:16 • Everything works now. – es1024 Aug 10 '14 at 8:24 Take 'em with me -- VB (what, no love?) This guy tries to hide while everyone else is shooting, and assuming he is still around when the truck rolls near, will try to nuke the healthiest looking target that is left standing. If he still sees the truck barreling down at him, then he takes the cowards way out. Module Module1 Dim players As String() Dim numTurns As Integer Public Sub Main(args() As String) 'split results into the different players players = args(1).Split(" ") 'how many turns have we been going? numTurns = players(0).Split(",").Length If numTurns = 48 Then 'can hear the truck Console.Write("P") ElseIf numTurns = 49 Then ' can see the truck Console.Write("T" & PlayerWithHighestHealth()) ElseIf numTurns = 50 Then 'can smell the truck Console.Write("S0") Else ' hiding is best Console.Write("D" & PlayerWithHighestHealth()) End If End Sub Private Function PlayerWithHighestHealth() As Integer Dim p As Integer = 1 Dim health As Integer = 1 For i = 1 To 3 If players(i).Split(",")(0) > health Then 'this is the healthiest looking guy p = i health = players(i).Split(",")(0) End If Next Return p End Function End Module  Run as Standoff.exe 5 "3,S2,N 5,P,N 3,S0,N -2,S3,N" for example • Don't forget about the first integer argument that nobody seems to bother with. The players history is actually the second argument. – comperendinous Aug 11 '14 at 19:06 • @comperendinous. Thanks. I don't know how I missed that little note about the first argument being the standoff #. Updated my code to get the right argument. – APrough Aug 11 '14 at 19:11 • It's no fun shooting at you if the scoring program is just assigning you "N" all the time (-; – comperendinous Aug 11 '14 at 19:15 • TakeEmWithMe.vb (7,23) : Error VBNC99999: After removing narrowing (except object) candidates for method 'Split', nothing was found – es1024 Aug 12 '14 at 0:27 # Equivocator The Equivocator finds those who are alive & closest in health to him and shoots them. function string:split( inSplitPattern, outResults ) if not outResults then outResults = { } end local theStart = 1 local theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart ) while theSplitStart do table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) ) theStart = theSplitEnd + 1 theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart ) end table.insert(outResults, string.sub( self, theStart ) ) return outResults end function RunRound() -- set up the players players = match:split(" ") myHealth = string.sub(players[1],1,1) closestHealth = 10 enemy = 3 if myHealth == "-" then return "N" end -- find someone with health close to mine for i=2,#players do moves = players[i] playerHealth = moves:sub(1,1) if playerHealth ~= "-" then if math.abs(playerHealth - myHealth) <= closestHealth then enemy = i-1 closestHealth = math.abs(playerHealth - myHealth) end end end return "S"..enemy end local args = {...} -- command line arguments match = args[2] -- store the set of matches action = RunRound() print(action)  ## SolidSnake - Java Take advantage of enemy down times. Dodge all other times. A little aggressive for Snake... But he's mad because Big Boss is in the spot light all the time. War... War never changes.... import java.util.Random; public class SolidSnake { public static void main(String[] args) { int target; char hp; Random rand = new Random(); if(args.length == 0) { target = rand.nextInt(3) + 1; System.out.println("D"+target); return; } String[] list = args[1].split(" "); for(int i = 1; i <= list.length; ++i) { hp = list[i].charAt(0); if(hp > 0) { char c = list[i].charAt(list[i].length()-1); String lastMove = String.valueOf(c); //Check for P if(lastMove.equals('P')) { target = i; System.out.println("S"+target); return; } //Check for S* if(Character.isDigit(lastMove.charAt(lastMove.length() - 1))) { target = i; System.out.println("S"+target); return; } } } //If not any P or S* for(int i = 1; i <= list.length; ++i) { hp = list[i].charAt(0); if(hp > 0) { target=i; System.out.print("D"+target); return; } } //Catch-safe System.out.print("D1"); return; } }  Compile/run: javac SolidSnake.java java SolidSnake arg0 arg1  • Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4 at SolidSnake.main(SolidSnake.java:19) when passed 30023 "5,S1 3,N 5,N 5,N" – es1024 Aug 18 '14 at 23:27 ## BiasedOne - Java Pun intended public class BiasedOne { public static void main(String[] args) { System.out.println("S1"); return; } }  Compile/run: javac BiasedOne .java java BiasedOne arg0 arg1  • I think you might have meant "S1" in stead of "S"+1 – overactor Aug 14 '14 at 15:34 • oops! ty @overactor i was in a rush :P – Josef E. Aug 14 '14 at 15:35 • This was Straight Shooter's original strategy, before Politically Correct Gunman came along. It'll be interesting to see if S1 does any better than S2, which is about as far from a winning strategy as it can get. There are a few bots which sweep through the numbers to choose a target, so I suppose it's possible the position of the fixed target might influence the outcome. – comperendinous Aug 14 '14 at 23:54 # Hippolyta This Amazon Warrior Shooter, like her namesake, is destined to be slain. Since we now have an S0, an S1, and an S2 bot, for completeness, here's an S3 bot to see if the position of the fixed target makes any significant difference to the result. void main () { print ("S3"); }  Compile as Vala: valac hippolyta.vala  Run as any other executable: ./hippolyta 3 "5 5 5 5"  # Label1Goto1 - Python I ran out of good names (or I'm too lazy to think about others), but I have been doing some relatively repetitive work nowadays, hence came the name of the lowest common denominator of a loop structure. The bot is relatively simple (in comparison to Spock, my previous work). Label 1, Dodge it, Prepare it, Shoot it, Wait it, Throw it, Goto 1. def IsAlive(player): return int(player[1].split(",")[0]) > 0 import sys info_list = sys.argv[2].split() current_turn = len(info_list[0].split(",")) action = "N" target_list = [] for player in zip(range(1,4),info_list[1:]): if IsAlive(player): target_list.append(player[0]) import random target = random.choice(target_list) round = (current_turn - 1) % 5 if round == 0: action = "D" + str(target) elif round == 1: action = "P" elif round == 2: action = "S" + str(target) elif round == 3: action = "N" elif round == 4: action = "T" + str(target) print(action)  Run it as: python label1goto1.py  # Less Simple Shooter This is basically the Simple Shooter but with the added directive to shoot himself before the truck comes. If, for some reason, another entry already uses this exactly strategy, then I will withdraw it. @history = map([split(",",$_)],split(" ",$ARGV[1]));$target = 1;
for(2..3){
if($history[$_][0] >= $history[$target][0]){$target =$_}
}
if(~~@{$history[0]} >= 50){$target = 0;
}
print "S\$target"


I run it like this:

perl lesssimpleshooter.plx 5 "-2,S0 -2,S1 -2,S2 5,N"