Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

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.

  • Prepare: Unhook your grenade and prepare to throw it.

    • 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

share|improve this question
1  
Do you have exactly one grenade, or do you have many? Can you have multiple grenades being prepared at a time? –  isaacg Aug 9 at 23:52
2  
@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 at 1:48
3  
Lesson to everyone : Don't drink and drive. –  Mark Gabriel Aug 11 at 1:56
8  
@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 at 2:20
3  
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 at 4:47

40 Answers 40

up vote 5 down vote accepted
+100

Observer

This guy analyses his enemies. The goal is to survive until only one "aggressive" opponent is left, and the kill that one in an epic standoff.

Compile: javac Observer.java Run: java Observer arg0 arg1

import java.util.List;
import java.util.ArrayList;
import java.util.Random;

class Observer {
    private static List<Integer> aggressiveEnemies = new ArrayList<>();
    private static List<Integer> enemyGrenadiers = new ArrayList<>();
    private static List<Integer> aliveEnemies = new ArrayList<>();

    public static void main(String[] args) {
        if (args[1].length() <= 7) { //first round
            Random rand = new Random();
            printResult("D" + (rand.nextInt(3) + 1));
        }
        String players[] = args[1].split(" ");

        if (truckIsOnWay(players[0])) {
            printResult("P");
        }       

        calcEnemyInfo(players);

        // end this standoff now
        if (truckWillHit(players[0])) {
            if (isGrenadier(players[0]))
                printResult("T" + aliveEnemies.get(0));
            else
                printResult("S0");
        }

        // shoot enemy who is not aggressive
        if (aggressiveEnemies.size() == 0) {
            printResult("S" + aliveEnemies.get(0));
        }

        // only one enemy to handle
        if (aggressiveEnemies.size() == 1) {
            String player = players[aggressiveEnemies.get(0)];
            if (isGrenadier(player)) {
                printResult("S" + aggressiveEnemies.get(0));
            } else if (shotLastTurn(player, aggressiveEnemies.get(0))) {
                //safe to shoot him without receiving damage
                printResult("S" + aggressiveEnemies.get(0));
            } else {
                printResult("D" + aggressiveEnemies.get(0));
            }
        }

        // multiple aggressive enemies
        if (enemyGrenadiers.size() > 0) {
            printResult("S" + enemyGrenadiers.get(0));
        } else {
            int id = aggressiveEnemies.get(0);
            for (int playerId : aggressiveEnemies) {
                if (!shotLastTurn(players[playerId], playerId)) {
                    id = playerId;
                }
            }
            printResult("D" + id);
        }
    }

    private static void printResult(String result) {
        System.out.print(result);
        System.exit(0);
    }

    private static boolean isAlive(String player) {
        return !(player.charAt(0) == '-' || player.charAt(0) == '0');
    }

    private static void calcEnemyInfo(String[] players) {
        for (int i = 1; i < players.length; i++) {
            if (isAlive(players[i])) {
                aliveEnemies.add(i);
                if (isAggressive(players[i], i)) {
                    aggressiveEnemies.add(i);
                }
                if (isGrenadier(players[i])) {
                    enemyGrenadiers.add(i);
                }
            }
        }
    }

    private static boolean truckIsOnWay(String player) {
        return player.length() - player.replace(",", "").length() == 48;
    }

    private static boolean truckWillHit(String player) {
        return player.length() - player.replace(",", "").length() == 49;
    }

    private static boolean isAggressive(String player, int id) {
        return (player.contains("S") || player.contains("P")) && !player.contains("S" + id);
    }

    private static boolean isGrenadier(String player) {
        return player.contains("P");
    }

    private static boolean shotLastTurn(String player, int id) {
        return player.charAt(player.length() - 2) == 'S' && !player.contains("S" + id);
    }
}
share|improve this answer

Grenadier

Guns are overrated. A true Scotsman's standoff goes like this:

  • Prepare
  • Throw at enemy with most health
  • Repeat (if by some miracle you're still alive)

While this seems trivial, it's probably not a terrible strategy. Since guns and grenades both have a two-turn cycle, this is by far the more efficient1 way to deal damage.

Of course, if all three opponents shoot me on the first round it's no good. But not much else would be, either.

public class Grenadier {
    public static void main(String[] args) {
        if(args.length < 2)
            return;
        String[] list = args[1].split(" ");
        if(list.length < 4)
            return;

        if(list[0].charAt(list[0].length()-1) != 'P'){
            System.out.print("P");
            return;
        }

        int target = 1;
        for(int i=2;i<4;i++)
            if(list[i].charAt(0)>list[target].charAt(0))
                target = i;

        System.out.print("T"+target);
    }
}

Compile/run in the standard Java way:

> javac Grenadier.java
> java Grenadier arg0 arg1

1 Pointless footnote

share|improve this answer
37  
hahaha the footnote –  proud haskeller Aug 9 at 23:16
    
I think throwing a grenade, and then shooting is more efficient. The chance of you surviving 4 turns with this strategy is numbingly low. But 3 maybe (yes, both take 2, but the second turn for shooting is after the action, not before) –  Cruncher Aug 11 at 14:05
    
@Cruncher You're probably right. Eric said the same thing in chat. I told him my guy doesn't believe in guns and is too stubborn to use that logic, so he posted that strategy. However, I still believe this to be more efficient, if we're strictly talking damage dealt. That doesn't mean it's more effective at winning the game. Even if I die on the third turn, my second grenade still goes off. So if I live until then, that's guaranteed 6+ damage to everyone, game over. –  Geobits Aug 11 at 14:20
    
@Geobits now that I think about it, this may be better. The thing that matters most is the delta between you and opponents. When the grenade blows up, you gain +3 delta with who you threw it at, and +0 with the rest. A net of +3. Shooting. Gains you a +2 delta with who you shoot. +0 with the rest. I think the problem is that you -3 with people that are already dead. You should shoot if anyone is dead :) –  Cruncher Aug 11 at 14:46
2  
@codebreaker Never played it. It's a real life reference. –  Geobits Aug 11 at 15:11

Asimov's Rule Number 0 Bot - Python

A robot may not harm humanity, or, by inaction, allow humanity to come to harm.

Pretty straight forward, he'll attack the first player he sees holding a grenade in order to protect the majority of humans. If no one is a threat to the majority of humans, he'll do nothing.

import sys

def total_humans_alive(humans):
  return sum([is_alive(human) for human in humans])

def is_alive(x):
  return int(x.split(",")[0]) > 0  

def is_threat_to_humanity(lastAction):
  return lastAction == "P"

action = "N"
threat_id = 1
humans = sys.argv[2].split()[1:];

if total_humans_alive(humans) == 3:
  for human in humans:
    if is_threat_to_humanity(human[-1]):
      action = "S" + str(threat_id)
      break
    threat_id= threat_id+ 1

print action

Run it like:

python rule0bot.py
share|improve this answer
2  
Your robot is illogical. If the player holding the grenade throws, humanity takes 8+3+3+3=17 damage. If you kill him with the shot, humanity takes 2+6+3+3+3=17 damage. In both scenarios, whoever has the grenade blow up on them takes 8 and all others take 3 (unless they were previously dead). Humanity as a whole is not affected. I still like it, though. +1 :D –  Geobits Aug 11 at 16:11
3  
Actually, the best scenario for humanity is to hope the grenade is tossed at the robot ;) –  Geobits Aug 11 at 16:14
    
@Geobits Not trying to stop someone who's a threat is against the nature of robots. It'll try to stop someone holding a grenade to prevent the majority (other two) from being hurt. Have you read I, Robot? This logic is backed by Little Lost Robot and The Evitable Conflict. –  William Barbosa Aug 11 at 16:15
    
I've read it, but what I'm saying is that shooting them doesn't stop it here. If he dies while holding the grenade, it still explodes. Not only that, but the total amount of damage done to humanity remains the same. You're doing direct harm to a human with no gain to humanity. –  Geobits Aug 11 at 16:17
2  
+1 I disagree with Kyle Kanos's vote and want to nullify it. Also, Geobits is wrong in assuming this does nothing to help humanity. Sure, humanity may not come out better in the worst case scenario, but if the other two players shoot the grenade wielding douche then they're all better for it. –  FreeAsInBeer Aug 11 at 21:24

Han Solo - Python

Han shot first. In this case, he'll shoot first by picking the closest target alive.

import sys

def is_alive(player):
  return int(player.split(",")[0]) > 0

closest_living_target = 1;

for player in sys.argv[2].split()[1:]:
  if is_alive(player):
    action = "S" + str(closest_living_target)
    break

  closest_living_target = closest_living_target + 1

print action

Run it like:

python hansolo.py

Note: This is the first thing I ever wrote in Python, so if you see any python-specific bad practices, please let me know.

share|improve this answer
1  
pep8 style suggests your method should be is_alive –  Daenyth Aug 11 at 15:02
4  
@WilliamBarbosa have a look at pep8, it's the python style guide that everyone uses. legacy.python.org/dev/peps/pep-0008 –  Daenyth Aug 11 at 15:57
2  
Congratulations on being the only bot with an average health greater than 0, in the 8/11 round. –  isaacg Aug 11 at 17:51
5  
IMO, "style guides" are for hair dressers, not programmers. –  Kyle Kanos Aug 11 at 18:39
2  
@KyleKanos It's nice to have some consistency, though. I mean, if half the developers of a project use camel case and the other half types like this, the result will be "blergh" –  William Barbosa Aug 11 at 19:00

EmoCowboy

Why wait to die? Just kill yourself now. Hopefully the rest of the fools will blow eachother up to much less than -2.

Score will normally be -2. Sometimes -4 if people decide to shoot me off the bat. Rarely more than that, which means this should beat several of the current submissions.

Python

print('S0')

python EmoCowboy.py

EDIT: This isn't a joke, which is generally why these emo submissions are frowned upon. This is a legitimate strategy. Being alive is deadly!

share|improve this answer

Pacifist

He's a real swell guy, just got caught up with the wrong crowd.

main = putStr "N"

Run as runghc pacifist.hs, but you might want to compile it with -O3 if efficiency is an issue.

share|improve this answer
1  
Please rename it Luigi and let's see if he'll win anything! –  William Barbosa Aug 10 at 0:50
1  
@WilliamBarbosa Luigi? Did you say Luigi? –  killmous Aug 10 at 1:01
7  
Lol as if -O3 makes a freakin' difference. –  tomsmeding Aug 10 at 16:42
    
@tomsmeding It's slow on the runghc side. Actually it's 10 times slower on my Linux box. –  Ray Aug 10 at 21:31
5  
That implies the existence of violence, an implication our pacifist is not prepared to handle –  killmous Aug 11 at 15:20

Monkey -- Python (First ever entry!)

Monkey see, monkey do. Will repeat exactly the last action taken by a random player.

import sys, random
targetData = sys.argv[2].split()[random.randint(0,3)]
print(targetData.split(',')[len(targetData.split(','))-1])

Can be run like this: "python monkey.py args" No extra steps required.

share|improve this answer
2  
I hope they weren't shooting at you! Python supports negative array indices, so you don't need to calculate the length and subtract one; just use -1 directly. –  comperendinous Aug 11 at 4:13
    
@comperendinous Say I'm S3 in their list. If I execute S3, it won't shoot me silly. Also, the -1 index will return the last element? If so, cool! I'll be sure to add it. –  Elias Benevedes Aug 11 at 4:18
    
And don't forget the first (integer) argument. You need argv[2] to get the players history. –  comperendinous Aug 11 at 19:48
    
Just hope you don't get matched up with Emo Cowboy. –  codebreaker Aug 13 at 14:47

Spock, in Python 3.x

This code is more of a though experiment (hence named after Spock because... he's a vulcan, and they're pretty good at these sort of things) but was fun building it nonetheless. The main reasoning behind all this code are assumptions a good, logical being, like Spock, would do, if given the game's rules:


The objective of this game is to maximize score, which would be done by everyone just standing still, which isn't possible, due to the truck.

  • One of the directives Spock has to follow is to prevent the truck from appearing, making sure that everyone but one is dead before the truck appears.

The way Spock plays in the rest of the game can be summed up by his famous quote: "The needs of the many outweigh the needs of the few". In other words, Spock has to make sure that the least amount of damage is suffered, by killing those that do it. How he does it:

  • If no player has prepped a grenade, target the least healthy player that's still playing.
  • If there are players that prepped grenades, from those target the least healthy.

The reasoning is that, by targeting the weakest players, we are terminating sources of damage. The reasoning behind the grenades is that they are going off regardless and they do less damage if they aren't thrown.


And so this bot works. I haven't tested extensively for input failures (so please warn me if something goes awry) but I'm confident I worked out most of the kinks. I based a small part of the code from the HanSolo bot but for the most part it's a tangled mess of code. Enjoy.

def IsAlive(player):
  return int(player[1].split(",")[0]) > 0
def IsTarget(player, target_health):
  return int(player[1].split(",")[0]) < target_health
def HasGrenade(player):
  max_range = max(-4,-current_turn)
  for foo in range(-1,max_range,-1):
    if "P" in player[1].split(",")[foo]:
      for bar in range(-1,foo-1,-1):
        if player[1].split(",")[bar] not in ["T0", "T1", "T2", "T3"]:
          return True
  return False

import sys
info_list = sys.argv[2].split()
current_turn = len(info_list[0].split(","))
action = "N"

def Startgame():
  global action

  target = 1
  target_health = 5
  grenade_list=[]

  for player in zip(range(1,4),info_list[1:]):
    if HasGrenade(player):
      grenade_list.append(player)

  if not grenade_list:
    foo_list = []
    for player in zip(range(1,4),info_list[1:]):
      foo_list.append(player)
    target_list = foo_list
  else:
    target_list = grenade_list

  # Choose the least healthy player
  for player in target_list:
    if IsAlive(player) and IsTarget(player, target_health):
      target = player[0]
      target_health = int(player[1][0])

  action = "S" + str(target)

def Endgame(turn):
  global action

  if turn in [47, 49]:
    # Check if in 2 moves he can do enough damage
    rem_health = 0
    for player in zip(range(1,4),info_list[1:]):
      if IsAlive(player): rem_health += player[0]

    if rem_health < 5:
      Startgame() # It's lazy, but it should work
      return
    else:
      action = "P"
      return

  if turn in [48, 50]:
    # If Spock shot someone before, it needs to shoot again
    if info_list[0].split(",")[-1] in ["S0", "S1", "S2", "S3"]:
      Startgame()
      return
    else:
    # There's no rule against throwing grenades to dead bodies, so if
    # possible it will be thrown there.    
      target = 1
      target_health = 5

      foo_list = []
      for player in zip(range(1,4),info_list[1:]):
        foo_list.append(player)
      target_list = foo_list

      for player in target_list:
        if IsTarget(player, target_health):
          target = player[0]
          target_health = int(player[1][1])

      action = "T" + str(target)
      return

if current_turn > 46:
  Endgame(current_turn)
else:
  Startgame()

print(action)

Run it like:

python spock.py

2014-08-12 - Minor bugfix regarding grenade detection
2014-08-14 - Minor bugfix regarding endgame, thanks to isaacg for pointing it out before

share|improve this answer
    
You're not allowed to shoot more than once every two rounds. Read the spec on shooting. –  isaacg Aug 11 at 17:52
    
@isaacg Thanks for the reminder (which does explain the behavior), but there seems to be some latent bugs. For instance in this Spock should have shot InputAnalyser because he had a live grenade (even if Solo would have more 2 health). –  Doktoro Reichard Aug 11 at 22:33
    
Traceback (most recent call last): File "./players/Spock/Spock.py", line 87, in <module>: Endgame(current_turn) File "./players/Spock/Spock.py", line 79, in Endgame: if IsTarget(player, target_health): File "./players/Spock/Spock.py", line 4, in IsTarget: return int(player[1].split(",")[0]) < target_health TypeError: unorderable types: int() < str() –  es1024 Aug 12 at 20:54
    
player[1][1] should be int(player[1][1]). –  isaacg Aug 12 at 22:38
    
@isaacg again, I thank you for the help. I would have done this sooner but I've been swamped with stuff. Spock was ultimately built upon a misguided concept on how this would play out, hence the relatively lower score he has gotten. I do have some ideas for new bots, but with so many I now have to make sure that the main idea is original. –  Doktoro Reichard Aug 14 at 22:05

Simple Shooter - Perl (fixed bug)

This bot shoots at the opponent with the most health. It's a very simple strategy, but I think it has a decent chance of actually doing well.

@history = map([split(",",$_)],split(" ",$ARGV[1]));
$target = 1;
for(2..3){
 if($history[$_][0] >= $history[$target][0]){$target = $_}
}
print "S$target"

This is how to run it using some example input:

perl simpleshooter.plx 7 "3,S2,N 5,P,N 3,S0,N -2,S3,N"
share|improve this answer
    
Wow. Simple and smart. –  Soham Chowdhury Aug 19 at 17:37

Politically Correct Gunman

Very politically correct, as it doesn't discriminate against anything. Thus it isn't very smart.

import random

array = ["P", "N", "S0", "S1", "S2", "S3", "D1", "D2", "D3", "T1", "T2", "T3"]

print(array[random.randrange(0,11)])

It... doesn't really matter what arguments get passed to it how. python politicallycorrectgunman.py

share|improve this answer
    
I don't think the square brackets are supposed to part of the output. Perhaps @es1024 can confirm that. And do you know about random.choice? It's great for these kind of selections. –  comperendinous Aug 10 at 0:31
    
there can't be anything before the action and target in the output, though anything after is ignored –  es1024 Aug 10 at 1:22
    
Does that look better @es1024? –  Undo Aug 10 at 1:39
    
@Undo Yes, works perfectly now –  es1024 Aug 10 at 2:25
6  
Couldn't you just use random.choice(array)? –  user2357112 Aug 10 at 13:10

Aggressor

Pulls on round one, throws at highest health opponent on round 2, shoots at highest health opponent thereafter.

#include <cstdio>
#include <cstring>

int main(int argc, char** argv){
   char* t;
   t = strtok(argv[2]," ");
   int len = strlen(t);
   int max = -50, maxP;
   for(int i=1;i<4;i++){
      int cur;
      t = strtok(NULL," ");
      if(t[0]=='-'){cur = -1*(t[1]-'0');}
      else{cur = t[0]-'0';}
      if(cur>max){
         max = cur;
         maxP = i;
      }
   }
   if(len == 1){printf("P\n"); return 0;}
   if(len == 3){printf("T%d\n",maxP); return 0;}
   printf("S%d\n",maxP);
   return 0;
}

Run this like ./agg ID "5 5 5 5".

share|improve this answer

Straight Shooter

He's a trained part of the cavalry and talks in many languages but, being blinkered, Straight Shooter can only see the one enemy in front of him. Being a horse, he doesn't understand that you have to wait between shots.

print('S2')

Perl, Python 2/3, Ruby: this horse really is a polygot entry.

I'm winning anyway. I can't lose. You can shoot me but you can't kill me. Mister Ed ain't got shit on me!

For an answer that has a little more thought (and some functional paradigm) put into it, see Twenty-Fourth and a Halfth Century.

share|improve this answer

Ninja

Just dodges randomly trying to avoid getting hit.

math.randomseed(os.time())
print("D"..tostring(math.random(4)-1))

run as

lua ninja.lua

Args are unnecessary, but can be added w/o issue.

share|improve this answer
2  
@KyleKanos will Ninja dodge its own shots? –  distilledchaos Aug 12 at 7:27
2  
@distilledchaos: ...yes, yes he will. –  Kyle Kanos Aug 12 at 11:14

Anti-grenadier

Grenades are bad. Very bad. So if anyone is preparing one, best thing to do is shoot them. Otherwise, we'll just hang out.

-- Antigrenadier
local args = {...}  -- command line arguments

match = args[2]     -- store the set of matches

-- why this isn't standard in Lua....
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

-- set up the players
players = match:split(" ")

-- search other players for someone who pulled a grenade
for i=2,#players do
   moves = players[i]
   -- test if person is alive
   if moves:sub(1,1) ~= "-" then
      -- cycle through all elements of the string
      for j=#moves,2,-1 do
         -- if found, shoot!
         if moves:sub(j,j) == "P" then
            print("S"..i-1)
            os.exit()
         end
      end
   end
end

-- otherwise we just relax
print("N")
share|improve this answer

Name: PriorityTargets

Shell Command: ruby PriorityTargets.rb 5 [game_state]

Language: Ruby V2.1.2

Description: PriorityTargets attempts to find common playstyles. It then decides, based on those playstyles, who it wants to attack and what weapon to use.

Note: First Code Golf submission! Much larger than the other submissions because I went a little crazy.

#!/usr/bin/env ruby

class PriorityTargets
  class PlayerAction
    SHOOT = 'S'
    DODGE = 'D'
    PREPARE_GRENADE = 'P'
    THROW_GRENADE = 'T'
    NOTHING = 'N'
    attr_accessor :action, :target

    def initialize(action_string)
        @action = action_string[0, 1]
        @target = self.has_target? ? action_string[1, 1].to_i : false
    end

    def to_s
      string = @action
      string << @target.to_s if self.has_target?
      string
    end

    def has_cooldown?
      [SHOOT].include? @action
    end

    def is_aggressive?
      [SHOOT, PREPARE_GRENADE, THROW_GRENADE].include? @action
    end

    def has_target?
      [SHOOT, DODGE, THROW_GRENADE].include? @action
    end
  end


  class Player
    attr_reader :identifier, :health, :history
    attr_accessor :playstyles

    def initialize(player_identifier, player_string)
      @identifier = player_identifier
      @playstyles = []

      player_info = player_string.split(',')
      @health = player_info.shift.to_i
      @history = parse_history(player_info)
    end


    def has_attacked?(player, round = nil)
      round ||= self.history.length - 1
      player.history[0, round].each do |turn|
        did_attack = true and break if turn.is_aggressive? && turn.has_target? && turn.target == player.identifier
      end
      did_attack ||= false
    end

    def is_holding_grenade?(round = nil)
      round ||= self.history.length
      turn_history = self.history[0, round]
      is_holding = false

      turn_history.each_with_index do |turn, curr_round|
        if turn.action == PlayerAction::PREPARE_GRENADE && curr_round >= round - 3
          is_holding = true if turn_history.drop(curr_round).select{|turn| turn.action == PlayerAction::THROW_GRENADE }.length == 0
        end
      end

      is_holding
    end

    def is_dead?; self.health <= 0; end
    def is_on_cooldown?
      return false if self.history.length == 0
      self.history.last.has_cooldown?
    end

    def turn_at_round(round); self.history[round-1]; end

    private

      def parse_history(history_array)
        parsed = []
        history_array.each {|action_string| parsed << PlayerAction.new(action_string) }
        parsed
      end
  end

  class PlayerList
    include Enumerable

    def initialize(player_list = [], filter_list = false)
      @list = player_list
      @filter = filter_list if filter_list
    end

    #Enumerable Methods
    def each
      list = @list.select{|player| @filter.include?(player.identifier) } if @filter
      list = @list unless @filter
      list.each {|player| yield(player) }
    end

    def <<(player); @list << player; end
    def [](key)
      player = @list.select{|player| @filter.include?(player.identifier) }[key] if @filter
      player = @list[key] unless @filter
      player
    end

    def length
      list = @list.select{|player| @filter.include?(player.identifier) } if @filter
      list = @list unless @filter
      list.length
    end

    def empty?; self.length == 0; end
    def not_empty?; self.length > 0; end

    def create_filtered_list(player_ids)
      new_player_list = PlayerList.new(@list, player_ids)
      new_player_list
    end

    #PlayerList Methods
    def includes_playstyle?(playstyle)
      (self.with_playstyle(playstyle).length > 0)
    end

    def have_executed_action?(action)
      action_found = false
      self.each {|player| action_found = true and break if player.history.select {|turn| turn.action == action}.length > 0 }
      action_found
    end

    def direct_damages(round = nil)
      round ||= self.first.history.length

      damage_list = {}
      @list.each {|player| damage_list[player.identifier] = 0 }

      if round >= 1
        @list.each do |player|
          player.history[0, round].each_with_index do |turn, curr_round|

            if turn.has_target?
              target_player = @list.select{|curr_player| curr_player.identifier == turn.target }.first
              target_turn = target_player.turn_at_round(curr_round)

              damage_list[turn.target] += 8 if turn.action == PlayerAction::THROW_GRENADE

              if turn.action == PlayerAction::SHOOT
                damage_list[turn.target] += 2 unless target_turn.action == PlayerAction::DODGE && target_turn.target == player.identifier
              end
            end
          end
        end
      end

      damage_list.select! {|key| @filter.include? key } if @filter
      damage_list
    end


    def filtered_with_condition(&condition_block)
      player_ids = []
      self.each {|player| player_ids << player.identifier if condition_block.call(player) }
      create_filtered_list(player_ids)
    end

    def on_cooldown; filtered_with_condition {|player| player.is_on_cooldown?} end
    def not_on_cooldown; filtered_with_condition {|player| !player.is_on_cooldown?} end

    def dead; filtered_with_condition {|player| player.is_dead?} end
    def not_dead; filtered_with_condition {|player| !player.is_dead?} end

    def with_playstyle(playstyle); filtered_with_condition {|player| player.playstyles.include?(playstyle)} end
    def not_with_playstyle(playstyle); filtered_with_condition {|player| !player.playstyles.include?(playstyle)} end

    def with_max_health(round = nil)
      round ||= self.first.history.length
      player_damages = direct_damages(round)
      filtered_with_condition {|player| player_damages[player.identifier] == player_damages.values.min }
    end

    def with_identifier(identifier)
      matches = self.with_identifiers([ identifier ])
      return nil if matches.empty?
      matches.first
    end

    def with_identifiers(identifiers)
      create_filtered_list(identifiers)
    end
  end

  class PlayerTypes
    GRENADIER = :GRENADIER
    COWBOY = :COWBOY
    SKIDDISH = :SKIDDISH
    AGGRESSOR = :AGGRESSOR
    DEFENSIVE = :DEFENSIVE
    ANTI_GRENADIER = :ANTI_GRENADIER
    PLAYSTYLE_ORDER = [GRENADIER, COWBOY, SKIDDISH, AGGRESSOR, DEFENSIVE, ANTI_GRENADIER]

    def initialize(player_list)
      @players = player_list
    end

    def analyze_playstyles
      return if @players.first.history.length == 0

      PLAYSTYLE_ORDER.each do |playstyle|
        check_fnc = "is_"+playstyle.to_s+'?'
        @players.each {|player| player.playstyles << playstyle if self.send(check_fnc, player) }
      end
    end

    def is_GRENADIER?(player)
      #Grenade on first turn
      #Used more than one grenade
      #Never used gun, only grenade
      shoot_count = player.history.count {|turn| turn.action == PlayerAction::SHOOT }
      grenade_count = player.history.count {|turn| turn.action == PlayerAction::PREPARE_GRENADE }

      profiled ||= true if player.history.first.action == PlayerAction::PREPARE_GRENADE
      profiled ||= true if grenade_count > 1
      profiled ||= true if shoot_count == 0 && grenade_count > 0
      profiled ||= false
    end

    def is_COWBOY?(player)
      #Never used grenade, only gun
      shoot_count = player.history.count {|turn| turn.action == PlayerAction::SHOOT }
      grenade_count = player.history.count {|turn| turn.action == PlayerAction::PREPARE_GRENADE }

      profiled ||= true if grenade_count == 0 && shoot_count > 0
      profiled ||= false
    end

    def is_SKIDDISH?(player)
      #Dodged more than once
      #Never hurts anybody
      dodge_count = player.history.count {|turn| turn.action == PlayerAction::DODGE }
      attack_count = player.history.count {|turn| turn.is_aggressive? }

      profiled ||= true if dodge_count > 1
      profiled ||= true if attack_count == 0 && player.history.length > 1
      profiled ||= false
    end

    def is_AGGRESSOR?(player)
      #Only shoots person >= most health
      profiled = false
      player.history.each {|turn| profiled = true if turn.is_aggressive? && turn.has_target? }

      player.history.each_with_index do |turn, round|
        if turn.is_aggressive? && turn.has_target?
          profiled = false if !@players.with_max_health(round).include? @players.with_identifier(turn.target)
        end
      end
      profiled
    end

    def is_DEFENSIVE?(player)
      #Only hurts people who hurt them first
      player.history.each {|turn| profiled = true if turn.is_aggressive? && turn.has_target? }

      player.history.each_with_index do |turn, round|
        if turn.is_aggressive? && turn.has_target?
          target_player = @players.with_identifier(turn.target)
          profiled = false unless target_player.has_attacked?(player, round)
        end
      end
      profiled ||= false
    end

    def is_ANTI_GRENADIER?(player)
      #After a Grenadier has been shown, only shoots grenadier
      shots_fired = 0
      shots_fired_while_holding = 0

      player.history.each_with_index do |turn, round|
        if turn.is_aggressive? && turn.has_target?
          target_player = @players.with_identifier(turn.target)
          shots_fired += 1
          shots_fired_while_holding += 1 if target_player.is_holding_grenade?(round)
        end
      end

      (shots_fired > 0 && shots_fired/2.0 <= shots_fired_while_holding)
    end
  end




  def initialize(game_state)
    players_info = game_state.split(' ')
    @player = Player.new(0, players_info.shift)
    @players = PlayerList.new
    @players << @player
    enemy_identifiers = []

    players_info.each_with_index {|info, index| @players << Player.new(index+1, info); enemy_identifiers << index+1; }

    @enemies = @players.with_identifiers(enemy_identifiers  )
  end

  def analyze_playstyles
    types = PlayerTypes.new(@players)
    types.analyze_playstyles
  end

  def find_dodge_target
    armed_aggressors = @enemies.with_playstyle(PlayerTypes::AGGRESSOR).not_on_cooldown().not_dead()

    if armed_aggressors.not_empty?
      return armed_aggressors.with_max_health().first if @players.with_max_health().include?(@player) && @players.with_max_health().length == 1
    end

    return @enemies[Random.rand(3)] if @player.history.length == 0
    nil
  end

  def find_target
    unarmed_aggressors = @enemies.with_playstyle(PlayerTypes::AGGRESSOR).on_cooldown().not_dead()
    anti_grenadiers = @enemies.with_playstyle(PlayerTypes::ANTI_GRENADIER).not_dead()
    grenadiers = @enemies.with_playstyle(PlayerTypes::GRENADIER).not_dead()
    cowboys = @enemies.with_playstyle(PlayerTypes::COWBOY).not_dead()
    skiddish = @enemies.with_playstyle(PlayerTypes::SKIDDISH).not_dead()
    defensive = @enemies.with_playstyle(PlayerTypes::DEFENSIVE).not_dead()

    if unarmed_aggressors.not_empty?
      return unarmed_aggressors.with_max_health().first if @players.with_max_health().include?(@player) && @players.with_max_health().length == 1
    end

    return anti_grenadiers.with_max_health().first if anti_grenadiers.not_empty?
    return grenadiers.with_max_health().first if grenadiers.not_empty?
    return cowboys.with_max_health().first if cowboys.not_empty?
    return skiddish.with_max_health().first if skiddish.not_empty?
    return defensive.with_max_health().first if defensive.not_empty?
    return @enemies.with_max_health().not_dead().first if @enemies.with_max_health().not_dead().length > 0
    nil
  end

  def find_weapon
    return PlayerAction::THROW_GRENADE if @player.is_holding_grenade?

    anti_grenadiers = @enemies.with_playstyle(PlayerTypes::ANTI_GRENADIER).not_dead()

    return PlayerAction::PREPARE_GRENADE if anti_grenadiers.empty? && @enemies.have_executed_action?(PlayerAction::PREPARE_GRENADE)
    PlayerAction::SHOOT
  end

  def make_decision
    dodge_target = self.find_dodge_target
    target = self.find_target
    weapon = self.find_weapon

    decision ||= PlayerAction.new(PlayerAction::NOTHING) if @player.is_on_cooldown? || @enemies.with_max_health().not_dead().length == 0
    decision ||= PlayerAction.new(PlayerAction::DODGE + dodge_target.identifier.to_s) if dodge_target
    decision ||= PlayerAction.new(weapon + target.identifier.to_s)
    STDOUT.write decision.to_s
  end
end

priority_targets = PriorityTargets.new(ARGV[1])
priority_targets.analyze_playstyles
priority_targets.make_decision
share|improve this answer
1  
I like your approach, I look forward to seeing how it will do. –  overactor Aug 17 at 18:10
    
Sadly, it seems that it had a bug in it which created a Grenadier. Ah well, will do better next time :) –  fingerco Aug 23 at 2:59

Coward -- Perl

Acts very cowardly. When he feels healthy, he chooses an enemy who does not feel so and shoots him. Bonus points for those enemies which were shooting the last turn (because they are known to be doing Nothing this turn and so be absolutely defenceless). When he feels not so good, he runs for cover to save his hide in, occassionally shooting someone.

#!/usr/bin/perl

@allinfo = map { [split/,/] } split / /, $ARGV[1];
@life = map { $_->[0] } @allinfo;
@action = map { @$_>1 ? $_->[-1] : () } @allinfo;

if($life[0] < 3 && rand() < .5 )
{
    printf "D%d", +(sort { ($life[$a]>0)*($action[$a] eq "N") <=> ($life[$b]>0)*($action[$b] eq "N") } 1..3)[2]
}
else
{
    @score = map { $life[$_]>0 ? (5/$life[$_] + 2*($action[$_] =~ /S./)) : 0 } 1..3;
    printf "S%d", +(sort { $score[$a] <=> $score[$b] } 1..3);
}

Pretty standard Perl code; save it in some file and then run perl file argument argument [...]. I have checked for syntax and it was OK, so I hope for no problems with this.

E: eliminated a potential for division by 0 error.

share|improve this answer

Ricochet - Perl

Simple strategies seem to do decently in this challenge, so here's another. It shoots a random living player. It has the added feature of committing suicide at the end to avoid the truck.

@history = map([split(",",$_)],split(" ",$ARGV[1]));
$health = $history[0][0];
@options = ();
for(1..3){
 if($history[$_][0] > 0){
  push(@options,$_);
 }
}
$target = @options[int(rand(~~@options))];
if(~~@{$history[0]} == 50){
 $target = 0;
}
print"S$target";

Run like so:

perl ricochet.plx 5 "-2,S0 -2,S1 -2,S2 5,N" 
share|improve this answer

Bomberman

Bot written in R, command line should be: Rscript Bomberman.R arg0 arg1
I realized after starting to write this bot that Geobits already made a grenadier but I think mine is significantly different, in that it checks its health is above 3 before preparing a grenade, throws it at the last shooter first, and the most healthy second, and if its health is below 3 it will dodge the dangerous player (neither dead nor shooter in the last round) or shoot one of the remaining player.

input <- commandArgs(TRUE)
history <- do.call(rbind,strsplit(scan(textConnection(input[2]),"",quiet=TRUE),","))
health <- as.integer(history[,1])
last_shooter <- which(grepl("S",history[-1,ncol(history)]))
last_prepare <- which(history[1,]=="P")
if(!length(last_prepare)) last_prepare <- -1
last_throw <- which(grepl("T",history[1,]))
if(!length(last_throw)) last_throw <- 0
most_healthy <- which.max(health[-1])
dead <- which(health[-1]<=0)
inoffensive <- c(last_shooter,dead)
danger <- which(!(1:3)%in%inoffensive)
alive <- which(!(1:3)%in%dead)
if(health[1]>3 & last_throw > last_prepare) out <- "P"
if(last_throw < last_prepare) out <- ifelse(length(last_shooter),paste("T",last_shooter[1],sep=""),paste("T",most_healthy[1],sep=""))
if(health[1]<=3 & last_throw > last_prepare){
    if(length(danger)){
        out <- paste("D",sample(danger,1),sep="")
    }else{
        out <- paste("S",sample(alive,1),sep="")
    }
}
cat(out)

Edit

There seems to be some communication problem between this bot and your controller since all logs that I looked showed that my bot only output N. So, here is the same bot but rewritten in Python, in the hope that if this one also have communications problem, someone will see it.
To be called with python Bomberman.py arg0 arg1.

import sys,re,random

history = sys.argv[2]
history = [k.split(",") for k in history.split()]
health = [k[0] for k in history]
last_turn = [k[-1] for k in history]
last_shooter = [i for i,x in enumerate(last_turn) if re.search(r'S[0-3]',x)]
last_prepare = [i for i,x in enumerate(history[0]) if x=='P']
if not len(last_prepare):
    last_prepare = [-1]

last_throw = [i for i,x in enumerate(history[0]) if re.search(r'T[0-3]',x)]
if not len(last_throw):
    last_throw = [0]

most_healthy = [i for i,x in enumerate(health) if x==max(health)]
dead = [i for i,x in enumerate(health) if x<=0]
inoffensive = last_shooter+dead
danger = [k for k in range(1,4) if k not in inoffensive]
alive = [k for k in range(1,4) if k not in dead]
if health[0]>3 and last_throw[-1] > last_prepare[-1]:
    out = 'P'

if last_throw[-1] < last_prepare[-1]:
    if len(last_shooter):
        out = 'T'+random.choice(last_shooter)
    else:
        out = 'T'+random.choice(most_healthy)

if health[0]<=3 and last_throw[-1] > last_prepare[-1]:
    if len(danger):
        out = 'D'+random.choice(danger)
    else:
        out = 'S'+random.choice(alive)

print(out)
share|improve this answer
    
The name of the bot is relatively weak, but I ran out of idea, if anyone can think of a better name, please comment :) –  plannapus Aug 11 at 9:53
    
GymnastBomber!! –  Cruncher Aug 11 at 14:12

Neo

Dodge a living player who did not shoot last turn. If everybody alive shot last turn, shoot a random living player. Suicide when you see headlights.

import java.util.Random;
public class Neo {
    public static void main(String[] args) {
        if(args.length < 2)
            return;
        String[] list = args[1].split(" ");
        if(list.length < 4)
            return;
        Random rand = new Random();
        int turn = list[0].split(",").length;
        if(turn == 49){
            System.out.print("S0");
            return;
        }
        int target=0;
        for(int i=1;i<4;i++)
            if(list[i].length()<2 || (list[i].charAt(0)!='-' && list[i].charAt(list[i].length()-2)!='S'))
                target=i;
        if(target>0){
            System.out.print("D"+target);
            return;
        }
        while(target<1){
            int i=rand.nextInt(3)+1;
            if(list[i].charAt(0)!='-')
                target=i;
        }
        System.out.print("S"+target);
    }
}

I don't expect much from this guy against grenade-chuckers, but against shooters it might work pretty well. We'll see.

share|improve this answer
    
I used some of your boilerplate code in my answer. I hope that's okay. –  overactor Aug 12 at 15:06
    
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.charAt(String.java:658) at Neo.main(Neo.java:17) –  es1024 Aug 12 at 20:55
    
@es1024 Should be good to go now, and won't do nothing on every first turn. –  Geobits Aug 18 at 15:43

Twenty-Fourth and a Halfth Century

This Python entry ducks and dodges until only passive players or a single aggressive player remains, then starts shooting. It hopes a passing martian takes care of grenadiers and drunken truck drivers.

Unless I've done something wrong, this is functional Python. It certainly doesn't look like the kind of Python I wrote before Haskell and friends found me, and I don't think I've mutated anything in place. But if you know better, please do tell me.

#!/usr/bin/env python
import sys
import random

## ==== Move Types ================================================== ##
def move_type (move):
    if "" == move:
        return "N"
    return move[0]

def is_passive_move (move):
    if "N" == move:
        return True
    if "D" == move_type (move):
        return True
    return False

def is_aggressive_move (move):
    return not is_passive_move (move)

def passive_moves (moves):
    return [m for m in moves if is_passive_move (m)]

def aggressive_moves (moves):
    return [m for m in moves if is_aggressive_move (m)]
## ================================================== Move Types ==== ##

## ==== Player Model ================================================ ##
class Player:
    def __init__ (self, number, health, moves):
        self.number = number
        self.health = health
        self.moves  = moves

    def last_move (self):
        if 0 == len (self.moves):
            return ""
        return self.moves[-1]

def player_from (number, player_string):
    x = player_string.split (",")
    health = int (x[0].strip ())
    moves = [move.strip () for move in x[1:]]

    return Player (number, health, moves)

def players_from (game_state):
    return [player_from (n, p) for (n, p) in
                                   zip (range(4), game_state.split ())]

def is_alive (player):
    return 0 < player.health

def i_am_dead (me):
    return not is_alive (me)

def can_shoot (player):
    return "S" != move_type (player.last_move ())

def is_passive (player):
    passive_move_count = len (passive_moves (player.moves))
    aggressive_move_count = len (aggressive_moves (player.moves))

    return passive_move_count > (aggressive_move_count + 1)

def players_who_can_breathe (players):
    return [p for p in players if is_alive (p)]

def players_who_can_shoot (players):
    return [p for p in players if can_shoot (p)]

def players_who_stand_around (players):
    return [p for p in players if is_passive (p)]
## ================================================ Player Model ==== ##

## ==== Actions ===================================================== ##
def shoot_randomly_at (possible_targets):
    chosen_target = random.choice (possible_targets)
    return "S{0}".format (chosen_target.number)

def dodge_one_of_the (potential_shooters):
    chosen_shooter = random.choice (potential_shooters)
    return "D{0}".format (chosen_shooter.number)

def do_nothing ():
    return "N"

def pick_move (game_state):

    players = players_from (game_state)
    me = players[0]
    enemies = players[1:]

    if i_am_dead (me):
        return do_nothing ()

    living_enemies = players_who_can_breathe (enemies)
    if 1 == len (living_enemies):
        return shoot_randomly_at (living_enemies)

    passive_enemies = players_who_stand_around (living_enemies)
    if len (living_enemies) == len (passive_enemies):
        return shoot_randomly_at (passive_enemies)

    potential_shooters = players_who_can_shoot (living_enemies)
    if 0 < len (potential_shooters):
        return dodge_one_of_the (potential_shooters)

    return do_nothing ()
## ===================================================== Actions ==== ##

if "__main__" == __name__:

    game_state = sys.argv[2]
    print (pick_move (game_state))

Run as:

python twenty-fourth-and-a-halfth-century.py 0 "5 5 5 5"
share|improve this answer

Scared

This submission is scared of everyone. But it's especially scared of some people. So it figures out who's the most dangerous, and shoots them. If multiple enemies look the most dangerous, it shoots at a random one.

import sys
import random


def is_alive(player):
    return int(player.split(",")[0]) > 0


# Anyone with a live grenade who is alive is dangerous
def is_dangerous(player):
    return player.count("P") > player.count("T") and \
        int(player.split(",")[0]) > 0


def health(player):
    return int(player.split(",")[0])


# Failing that, healthy people are dangerous
def danger_rating(player):
    return 6 if is_dangerous(player) else health(player)

enemies = sys.argv[2].split()[1:]

highest_danger = max(danger_rating(enemy) for enemy in enemies)
most_dangerous_enemy = random.choice(
    [enemy_num+1 for enemy_num in range(len(enemies))
     if danger_rating(enemies[enemy_num]) == highest_danger])

print("S"+str(most_dangerous_enemy))

This is python (2 or 3, same result in either.) Save as scared.py, run with python3 scared.py

share|improve this answer

Manipulative Bastard – Python

Prepares and throws grenades. If he thinks there's no time or there are too few enemies, he shoots. If he's alone he tries to outsmart the other guy.

import sys

def health(p):
    return int(p[0])

def is_alive(p):
    return health(p) > 0

def can_act(p):
    return is_alive(p) and p[-1][0] != 'S'

def can_throw(p):
    return is_alive(p) and p[-1][0] == 'P'

def shot_first(p):
    if len(p) == 1:
        return False
    return p[1][0] == 'S'

def act(a):
    print a
    sys.exit(0)

player = sys.argv[2].split()[0].split(',')
enemies = [e.split(',') for e in sys.argv[2].split()[1:]]
healthiest = sorted(enumerate(enemies, 1), key=lambda e:health(e[1]))[-1]
alive = sum(is_alive(e) for e in enemies)

if alive == 1:
    i, e = healthiest
    if health(e) <= 2 and not can_act(e):
        act('S%d' % i)
    if can_throw(player):
        act('T%d' % i)
    if can_throw(e):
        act('S%d' % i)
    if can_act(e) and shot_first(e) and len(player) < 40:
        act('D%d' % i)
    if len(player) > 45:
        act('P')
    act('S%d' % i)

if can_throw(player):
    i, e = healthiest
    act('T%d' % i)

if len(player) > 45:
    act('P')

if health(player) <= 2 or any(can_throw(e) for e in enemies) or alive == 2:
    i, e = healthiest
    act('S%d' % i)

act('P')
share|improve this answer

Sniper - Lua

On the first turn it will shoot a random person, then it will shoot any players that it can kill (2 or 1 health). If neither of those work it will try to shoot the player that shot it last, otherwise it will shoot a random player. Run with lua Sniper.lua

turns = arg[2]
health = string.sub(turns, 1, 1)
--make random numbers random
math.randomseed(io.popen("date +%s%N"):read("*all"))
math.random(); math.random(); math.random()
function Split(str, delim, maxNb)
    -- Eliminate bad cases...
    if string.find(str, delim) == nil then
        return { str }
    end
    if maxNb == nil or maxNb < 1 then
        maxNb = 0    -- No limit
    end
    local result = {}
    local pat = "(.-)" .. delim .. "()"
    local nb = 0
    local lastPos
    for part, pos in string.gmatch(str, pat) do
        nb = nb + 1
        result[nb] = part
        lastPos = pos
        if nb == maxNb then break end
    end
    -- Handle the last field
    if nb ~= maxNb then
        result[nb + 1] = string.sub(str, lastPos)
    end
    return result
end
enemies = Split(turns, " ")
--first turn
if #enemies[1] == 1 then
  print(string.format("S%i",math.random(1,3)))
  os.exit()
end
--kills if possible
for enemy=1,3 do
  if (tonumber(string.sub(enemies[enemy + 1],1,1)) or 0) < 3 and string.sub(enemies[enemy + 1],1,1) ~= "-" then
    print(string.format("S%i",enemy))
    os.exit()
  end
end
--shoots the last person that shot at it
for enemy=1,3 do
  if string.sub(enemies[enemy + 1],#enemies[enemy + 1]-1) == "S0" and tonumber(string.sub(enemies[enemy + 1],1,1)) > 0 then
    print(string.format("S%i",enemy))
    os.exit()
  end
end
--otherwise shoot a random alive person
local aliveEnemies = {}
for enemy=1,3 do
  if string.sub(enemies[enemy + 1],1,1) ~= "-" then
    aliveEnemies[#aliveEnemies+1]=enemy
  end
end
print(string.format("S%i",math.random(1,#aliveEnemies)))
share|improve this answer
    
It'll actually be run with an extra argument first; e.g., lua Sniper.lua 3 "5,S1 3,D3 5,N 5,P". You might need to check your arg index. –  comperendinous Aug 11 at 19:35
    
@comperendinous, thanks, fixed now –  waylon531 Aug 11 at 21:03
    
Hi, @waylon531, question about Lua: the randomseed math.randoms "math.randomseed(os.time()) math.random(); math.random(); math.random()" isn't enough to randomize the script? –  AndoDaan Aug 11 at 22:13
1  
AndoDaan, according to lua-users.org/wiki/MathLibraryTutorial some OS's always return the same number the first time math.random() is called. –  waylon531 Aug 11 at 23:16
    
lua: ./players/Sniper/Sniper.lua:38: attempt to compare nil with number stack traceback: ./players/Sniper/Sniper.lua:38: in main chunk [C]: in ? –  es1024 Aug 12 at 20:56

Darwin

Survival of the fittest means the least healthy must die.

Rationale

Looking at the batch of results from Tuesday (12th), there seem to be three distinct groupings: survivors; the effectively suicidal; and the worse than useless. The survivors share simple shooting-based strategies. While a couple of other bots (Spock, Coward) will target the least healthy enemy, they also complicate their strategies with other actions. This one does not. Like Simple Shooter, it has a clear definition of the target and sticks with it relentlessly. It'll be interesting to see where it fits into the results.

#!/usr/bin/env python

import sys
import random

## ==== Player Model ================================================ ##
class Player:
    def __init__ (self, number, health):
        self.number = number
        self.health = health

def player_from (number, player_string):
    x = player_string.split (",")
    health = int (x[0].strip ())

    return Player (number, health)

def players_from (game_state):
    return [player_from (n, p) for (n, p) in
                                   zip (range(4), game_state.split ())]

def is_alive (player):
    return 0 < player.health

def i_am_dead (me):
    return not is_alive (me)

def players_who_can_breathe (players):
    return [p for p in players if is_alive (p)]

def players_by_health (players):
    return sorted (players, key=lambda p: p.health)

def least_healthy_players (players):
    sorted_living_players = \
        players_by_health (players_who_can_breathe (players))
    lowest_living_health = sorted_living_players[0].health
    return [p for p in players if lowest_living_health == p.health]
## ================================================ Player Model ==== ##

## ==== Actions ===================================================== ##
def shoot_randomly_at (possible_targets):
    chosen_target = random.choice (possible_targets)
    return "S{0}".format (chosen_target.number)

def do_nothing ():
    return "N"

def pick_move (game_state):
    players = players_from (game_state)
    me = players[0]
    enemies = players[1:]

    if i_am_dead (me):
        return do_nothing ()

    least_healthy_enemies = least_healthy_players (enemies)
    return shoot_randomly_at (least_healthy_enemies)
## ===================================================== Actions ==== ##

if "__main__" == __name__:

    game_state = sys.argv[2]
    print (pick_move (game_state))

This is a stripped-down, slightly modified version of my earlier Twenty-Fourth and a Halfth Century, and shares its invocation:

python darwin.py 3 "5 5 5 5"
share|improve this answer

Zaenille - C

Priorities :

  1. Shoot if it's a 1 on 1 left
  2. Shoot grenadiers
  3. Dodge
  4. Nothing (just to confuse some)

Compile with gcc <filename.c>.

Run with ./a.out <parameters>.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
    char* input = argv[2];
    int enemyCount=1;
    int aliveCount=0;
    int aliveEnemy=0;

    //default
    char action = 'N';
    int target = NULL;

    const char delim[1] = " ";
    char *token;

    //first turn
    if(strcmp(input,"5 5 5 5")==0){
        printf("D1");
        return 0;
    }

    token = strtok(input, delim);
    token = strtok(NULL, delim); //skip to the first enemy

    while(token != NULL){
        //if someone is alive :
        if(strstr(token,"-")==NULL && token[0]!='0'){
            aliveCount++;
            aliveEnemy=enemyCount;
            //if that person did nothing this turn, take it as a tip that he will shoot next turn, and dodge
            if(strstr(token, "N")!=NULL){
                action = 'D';
                target=enemyCount;
            }

            //if that person prepared a grenade, shoot him down
            if(strstr(token, "P")!=NULL){
                action = 'S';
                target=enemyCount;
            }
        }

        token = strtok(NULL, delim);
        enemyCount++;
    }

    //if there is 1 enemy left, shoot him down
    if(aliveCount==1){
        action='S';
        target=aliveEnemy;
    }

    printf(action=='N'?"N":"%c%d",action,target);

    return 0;
}
share|improve this answer
1  
The first (integer) argument doesn't indicate the round count, if the examples given in the question are anything to go by. You wouldn't want to shoot yourself on the first turn just because you'd been assigned to standoff number 82. –  comperendinous Aug 11 at 19:43
    
Really? D: Thanks @comperendinous. Will edit the code. –  Mark Gabriel Aug 11 at 23:55

InputAnalyzer

The key a game such as this is analyze how all your opponents are playing to respond accordingly. My bot will do just that using complicated algorithms that will result in using my opponents turns to my advantage giving a decisive victory!

Edit: I now

  1. dodge any player who has a live grenade
  2. will no longer attempt to shoow/throw/dodge myself.

import System.Environment   
import Data.Char (ord)
import Data.List.Split

main = do 
    args <- getArgs
    let secondArg = (last args)
    let opt = (argCount secondArg 0)
    let list = (splitOn " " secondArg)
    let enemysToCheck = [1,2,3]
    let target = (avoidShootingSelf (findTarget (last args) 0 0 0 0))
    putStrLn (decide list enemysToCheck opt target)

argCount :: String -> Int -> Int
argCount (s:str) i
    |(length str) == 0 = i `mod` 4
    | otherwise = (argCount str (i + (ord s)))

--myPseudo takes number 0-3, and a possible target and translates it to a command 
myPseudo :: Int -> Int -> String
myPseudo 0 b = "S" ++ (show b)
myPseudo 1 b = "D" ++ (show b)
myPseudo 2 b = "P"
myPseudo 3 b = "T" ++ (show b)

decide :: [String] -> [Int] -> Int -> Int -> String
decide [] [] a b = (myPseudo a b)
decide (x:xs) [] a b = (myPseudo a b)
decide xs (y:ys) a b
    | (liveGrenade z 0) == True = "D" ++ (show y)
    | otherwise = (decide xs ys a b)
    where z = xs!!y

--checks if a player has a live grenade
liveGrenade :: String -> Int -> Bool
liveGrenade [] a = a > 0
liveGrenade (s:str) a
    | s == 'T' = (liveGrenade str (a - 1))
    | s == 'P' = (liveGrenade str (a + 1))
    | otherwise = (liveGrenade str a)

--findTarget picks a target by doing some irrelevant string processing on the 2nd argument
findTarget :: String -> Int -> Int -> Int -> Int -> Int
findTarget [] a b c d = ((maximum [a,b,c,d]) `mod` 4)
findTarget (s:str) a b c d
    | s == 'S' = (findTarget str (a + 1) b c d)
    | s == 'D' = (findTarget str a (b + 1) c d)
    | s == 'P' = (findTarget str a b (c + 1) d)
    | s == 'T' = (findTarget str a b c (d + 1))
    | s == 'N' = (findTarget str a b c (d + 1))
    | otherwise = (findTarget str a b c d)

--Makes sure I do target myself takes int 0-3
avoidShootingSelf :: Int -> Int
avoidShootingSelf 0 = 1
avoidShootingSelf a = a

Compile the bot with the following command (Need to have ghc)

ghc --make InputAnalyzer.hs

Shell Command to run should be the following

./InputAnalyzer

Note: I tested on windows so if you have any trouble regarding compling/running please say so in comment in and I will do my best to find out the correct command.

share|improve this answer
1  
Well, I suppose that's one way of getting a weighted pseudorandom generator in Haskell. –  comperendinous Aug 10 at 0:59

Dog named Courage

First thing - shoot the bad guys on sight. Then dodge randomly until someone prepares a grenade. Then when everyone shoots at him, prepare my own grenade and throw it on anyone. But the distraction man.

Edit: Now implemented as I thought It should be. Before, the score was: 35.9

Updated: Sometimes shoots instead of dodgeing

couragethedog.py

import sys
from collections import defaultdict as ddict
from random import choice
args = sys.argv
info = " ".join(args[2:]).strip('"').split(" ")
players = ddict(dict)
for i,s in enumerate(info):
    parts = s.split(",")
    players[i]["health"]=int(parts[0])
    players[i]["last"]=parts[-1]
    players[i]["history"]=parts[1:]
    players[i]["turn"]=len(parts)
me=0
others=[1,2,3]
turn=players[me]["turn"]
alive = filter(lambda p:players[p]["health"]>0,others)
def act():
    if turn is 1:
        return "S%d" % choice(alive)
    if "P" == players[me]["history"][-1]:
        targets = set(alive)
        for i in alive:
            if "P" == players[i]["history"][-2]:
                targets.remove(i)
        return "T%d" % choice(list(targets))
    for i in others:
        if players[i]["history"][-1] is "P":
            return "P"
    if choice([True,False,False]):
        return "S%d" % choice(alive)
    return "D%d" % choice(alive)
print act()

Run as

python couragethedog.py
share|improve this answer

MAD - Java

The MAD bot trust in the power of intimidation through mutual assured destruction. Whenever he doesn't have a ready grenade, he prepares one. He then dodges possible gunners until either someone tries to deal damage to him or his grenade is about to explode. From the moment he gets attacked, he chucks grenades at whoever has tried to deal more damage to him this match so far. If his grenade is about to explode, he bombs the leading player. MAD is not against taking a shot at someone when there's nothing to dodge or directly chuck a grenade at and his grenade is still good for at least one turn.

    import java.util.ArrayList;
import java.util.Random;

public class MAD 
{
    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 grenadeExplodes = grenadeExplodes(players[0]);        
        if(grenadeExplodes==-1)
        {
            System.out.print("P"); // I don't feel safe without a prepared grenade in my hand
            return;
        }

        int highestDamage = -1;
        int playerToShoot = -1;        
        for(int i=1; i<4; i++) // did anyone try to hit me?
        {
            int damage = damageAttempted(players[i], 0);
            if(isAlive(players[i]) && (damage>highestDamage || (damage==highestDamage && rand.nextDouble()>0.5)))
            {
                highestDamage = damage;
                playerToShoot = i;
            }           
        }

        if(highestDamage > 0)
        {
            System.out.print("T" + Integer.toString(playerToShoot)); // don't tell me I didn't warn you
            return;
        }

        int highestHealth = -1;
        int healthiestPlayer = -1;      
        for(int i=1; i<4; i++) // who is doing too well for their own good?
        {
            int health = getHealth(players[i]);
            if(health>highestHealth || (health==highestHealth && rand.nextDouble()>0.5))
            {
                highestHealth = health;
                healthiestPlayer = i;
            }
        }

        if(grenadeExplodes==0)
        {
            System.out.print("T" + Integer.toString(healthiestPlayer).charAt(0)); // get this hot head outta here!!
            return;
        }

        // I've got time to flaunt my grenade around

        ArrayList<Integer> playersToDodge = new ArrayList<Integer>();       
        for(int i=1; i<4; i++) // lets see who could shoot me
        {
            if(canMove(players[i]) && grenadeExplodes(players[i])!=0)
            {
                playersToDodge.add(i);
                if(grenadeExplodes(players[i])==-1) // players who have no grenade are more likely to shoot
                {
                    playersToDodge.add(i);
                }
            }
        }

        if(playersToDodge.size()>0)
        {
            System.out.print("D" + Integer.toString(playersToDodge.get(rand.nextInt(playersToDodge.size() - 1))).charAt(0)); // what do we say to would-be gunners?
            return;
        }

        if(grenadeExplodes!=1)
        {
            System.out.print("S" + Integer.toString(healthiestPlayer).charAt(0)); // seems like I can take a free shot at someone
        }
        else
        {
            System.out.print("N"); // wouldn't want to end up with an exploding grenade in my hand while being unable to throw it.
        }

    }

    public static boolean isAlive(String player) 
    {
        return player.charAt(0)!='-'; 
    }

    public static boolean canMove(String player)
    {
        return isAlive(player) && 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 damageAttempted(String player, int target)
    {
        String[] actions = player.split(",");
        int damage = 0;
        char targetChar = Integer.toString(target).charAt(0);
        for(int i=0; i<actions.length; i++)
        {
            if(actions[i].charAt(0)=='S' && actions[i].charAt(1)==targetChar)
            {
                damage += 2;
            } 
            else if (actions[i].charAt(0)=='T')
            {
                if(actions[i].charAt(1)==targetChar)
                {
                    damage += 8;
                }
                else
                {
                    damage += 3;
                }
            }
        }

        return damage;
    }

    public static int getHealth(String player)
    {
        return Integer.parseInt(player.split(",")[0]);
    }
}

This Bot will likely perform poorly, but I liked the idea anyway. MAD would probably do better in a field with smarter bots that log the behavior of other bots and with more matches run between 4 bots.

share|improve this answer
    
Some credit shoudl go to Geobits, I stole some boiler plate code of his Neo entry. –  overactor Aug 12 at 15:05
    
You didn't take much, no credit needed :) –  Geobits Aug 12 at 15:32
    
Calling java MAD 43 "5 5 5 5" appears to output nothing. –  es1024 Aug 18 at 2:16

Sadist

python

His priority is to cause pain and grenades hurt. He pulls first turn. He likes to kill when you can't attack. He toys with SSSs (single simple shooters) by dodging and pulling to prolong the domination. He even choses to attack those first who've done nothing to anyone.

Because he uses grenades, he (and everyone else) will ususally not survive the second or third round. If he is paired up with another grenadeer, everyone will die. This means I don't expect to win but I wrote this to learn python (never used it before and I'm trying to get an introduction into a bunch of new languages). There are several other, "pull firsts" so if you feel it is too simular let me know. The others don't seem to be willing to pull and then dodge, however.

import sys    

def ident(thatguy):

    return int(thatguy.split(",")[0])

def health(thatguy):
    return int(thatguy.split(",")[1])

def shooter(thatguy):
    if(len(thatguy.split(","))<3):
        return 1==1
    else: return thatguy.split(",")[2][0]=="S"

def mosthealth(group):
    bigbad=group[0]
    for foe in group:
        if (health(foe)>health(bigbad)): bigbad=foe
    return bigbad

def igotanuke(mine):
    return mine.count("P")-mine.count("T")>0

def guytonuke(allguys,fighters):
    backup=allguys[:]
    for Aguy in backup:
        if(health(Aguy)<4):
            allguys.remove(Aguy)
            if (shooter(Aguy)): fighters.remove(Aguy)

    if(len(allguys)==0): return mosthealth(backup)
    if (len(allguys)==len(fighters)):
        return mosthealth(allguys)
    else:
        for fighter in fighters: allguys.remove(fighter)
        return mosthealth(allguys)

raw = sys.argv[2]
player = raw.split(" ")
thisisme=player.pop(0)
turn = len(player[0].split(","))-1

guys=[]
gunners=[]
c=1
for dude in player:
    dude=str(c)+","+dude
    c+=1
    if (health(dude)>0): 
        guys.append(dude)
        if (shooter(dude)):
            gunners.append(dude)

if (turn==0): print "P"
elif(turn==49): print"S0"
elif(igotanuke(thisisme))&( turn % 2 == 1): print "T"+str(ident(guytonuke(guys,gunners)))
elif(len(guys)<2)&(len(gunners)>0) & (turn % 2 == 1): print P
elif(turn % 2 == 0) & (len(gunners)>0): print "D"+str(ident(mosthealth(gunners)))
elif(turn % 2 == 1) & (len(gunners)>0): print "S"+str(ident(mosthealth(gunners)))
else: print "S"+str(ident(mosthealth(guys)))
share|improve this answer
    
I don't think that raw_input is going to work. sys.argv[2] seems to be the consensus for Python entries. You might also find use for pop, which would allow you to condense thisisme=player[0];player.remove(player[0]) into the simpler thisisme=player.pop(0). –  comperendinous Aug 18 at 19:17
    
@comperendinous I was testing the code at Ideone and sys.argv doesn't work at all (likely due to importing sys). That is why i used raw_input. Is there a difference that would cause the latter not to work? If so I likely will need to find another online compiler for python. Thanks for the suggestion with pop! I didn't realise that command allow the index to be specified. I will use it for any future python code. –  kaine Aug 18 at 19:27
1  
raw_input pulls from STDIN, but the player history is passed to your program as a command-line argument, which is why you need sys.argv. For the sake of testing, you could just set it manually with sys.argv = ["sadist.py", "0", "5 5 5 5"]. Then you should be able to call player=sys.argv[2].split(). If importing sys really is impossible, for testing you could even drop the dot and call the array sysargv. So long as everything else works and you go back to sys.argv in your submission, it should be fine. –  comperendinous Aug 18 at 19:43
    
@comperendinous to confirm, if I call sys.argv it will return as an array the name of program in 0, that single number in 1 and the actual part I use in 2? They are all strings. With that info I should be able to edit it properly. Thank you very much! –  kaine Aug 18 at 19:46

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 
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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