text:
KOTH: A world-wide pandemic
Final results are here !
Introduction
In 2042, the world has become overpopulated. Globalization, overcrowding, new lifestyles and a global lack of hygiene has caused a new pandemic to spread. During those hard times, state leaders have to manage the situation. You can't let your population be decimated, but maybe you could benefit by letting your neighbors to die...
Glossary

Healthy: People not infected
Infected: People who can die from the pandemic
Dead: Body count, no particular effect (only scoring)
Infection Rate: Number of Healthy who will become Infected each turn
Contagion Rate: Percentage of Infected that will convert Healthy to Infected each turn
Lethality Rate: Percentage of Infected that will die each turn
Migration Rate: Percentage of both Healthy and Infected that will emigrate/immigrate each turn
Local: Affects only your state
Global: Affects every state

Principle
Each of the players will manage one town, starting with 100 people. Unfortunately, among them is one Infected.
The game is turn-based. A turn consists of seven phases, the last one being interactive (asking bots for a command). The order of the players is randomized each turn. The next phase begins when the previous phase has been executed by every town (Turn 1: Player 1, Player 2, Player 3... ; Turn 2: Player 3, Player 2, Player 1...) :
1. Mutation                                 - AUTOMATED
2. Reproduction                             - AUTOMATED
3. Migration                                - AUTOMATED
4. Infection                                - AUTOMATED
5. Contagion                                - AUTOMATED
6. Extinction                               - AUTOMATED
7. Players Turn                             - INTERACTIVE

The controller provides you with input via command arguments, and your program has to output via stdout.
Syntax
Input
Each time your program is called, it will receive arguments in this format:
Round;YourPlayerId;PlayerId_Healthy_Infected_Dead_InfectionRate_ContagionRate_LethalityRate_MigrationRate;PlayerId_Healthy_Infected_Dead_InfectionRate_ContagionRate_LethalityRate_MigrationRate;...
Round are 1-indexed.
Example input
6;2;1_106_23_9_2_4_13_5;0_20_53_62_16_20_35_5;2_20_53_62_16_20_35_5
Here, you see it is the 6th round and you are player 2. You have 20 healthy, 53 infected, 62 dead, 16% infection rate, 20% contagion rate, 35% lethality rate, and a 5% migration rate.
Output
You have to output three characters (no space, no separator), which each correspond to one action you'll take this turn. The order of the characters determine the order of the actions. You can output the same actions multiple times.
N: Do Nothing
M: Research Microbiology [Effects: Reduce local Infection Rate by 4%]
E: Research Epidemiology [Effects: Reduce local Contagion Rate by 8%]
I: Research Immunology [Effects: Reduce local Lethality Rate by 4%]
V: Research Vaccination [Effects: Reduce local Infection Rate by one, reduce local Contagion Rate by 4%, reduce local Lethality Rate by 2%]
C: Give Cure [Effects: Convert 10 local Infected to Healthy]
Q: Quarantine [Effects: Remove 30 local Infected]
O: Open Borders [Effects: Increase local Migration Rate by 10%]
B: Close Borders [Effects: Decrease local Migration Rate by 10%]
T: BioTerrorism [Effects: Convert 4 global Healthy to Infected]
W: Weaponization [Effects: Increase global Infection Rate by 1, increase global Lethality Rate by 2%]
D: Dissemination [Effects: Increase global Infection Rate by 1, increase global Contagion Rate by 2%]
P: Pacification [Effects: Decrease global Infection Rate by 1, decrease global Contagion Rate by 1%, decrease global Lethality Rate by 1%]
Gameplay
All phases
Invalid command = Nothing
Percentage are added up like integers, i.e. 10% - 4% = 6%. When percentage are applied in a formula, the result is floored.
Phase 1: Mutation
The pandemic is becoming more potent. Each turn, it randomly gains one of these attributes (this mutation affects all players at once):

Increase global Infection Rate by 2
Increase global Contagion Rate by 5%
Increase global Lethality Rate by 5%

Phase 2: Reproduction
Every five rounds (round 5, 10, 15...), new citizens will be born. Each pair of Healthy will make one Healthy (23 Healthy generate 11 new Healthy). Each pair of Infected will make one Infected.
Phase 3: Migration
Each turn, a percentage of Healthy and Infected will leave states, depending on their Migration Rate (10 Healthy will leave a state with 100 Healthy and 10% Migration Rate). Then, the migrants will be distributed among every state, once again depending on Migration Rate. (The rates of each state are weighted and migrants are then all distributed accordingly).
Phase 4: Infection
Healthy of each state are converted to Infected, according to Infection Rate.
Phase 5: Contagion
Healthy of each state are converted to Infected, according to Contagion Rate. The number is calculated by multiplying the Infected by the Contagion Rate.
Phase 6: Extinction
Infected are converted to Dead, according to Lethality Rate. The number is calculated by multiplying the Infected by the Lethality Rate.
Phase 7: Players turn
Each player receive input and must output three actions, that are executed in the order they are output.
Rules

Bots should not be written to beat or support specific other bots.
Writing to files is allowed. Please write to "yoursubmissionname.txt", the folder will be emptied before a game starts. Other external resources are disallowed.
Your submission has one second to respond (per town).
Provide commands to compile and run your submissions.

Winning
Winner is the one with the most Healthy after 50 rounds. If a player is the last alive (more than 0 Healthy or Infected) the game stops and he wins. If multiple players have the same amount of Healthy, the one with most Infected will win, then the one with fewer Deads.
Controller
You can find the controller on GitHub. It also contains three samplebots, written in Java.
To make it run, check out the project and open it in your Java IDE. The entry point in the main method of the class Game. Java 8 required.
To add bots, first you need either the compiled version for Java (.class files) or the sources for interpreted languages. Place them in the root folder of the project. Then, create a new Java class in the players package (you can take example on the already existing bots). This class must implement Player to override the method String getCmd(). The String returned is the shell command to run your bots. You can for example make a Ruby bot work with this command : return "C:\Ruby\bin\ruby.exe MyBot.rb";. Finally, add the bot in the players array at the top of the Game class.
Final Results  (2016-03-04 08:22 GMT)
Global (100 reputation) :
100 games results : http://pasted.co/942200ff
1. EvilBot (24, 249, 436)
2. Triage (23, 538, 486)
3. WICKED (23, 537, 489)
4. Israel (23, 40, 240)
5. InfectedTown (22, 736, 482)
6. ZombieState (22, 229, 369)
7. Mooch (22, 87, 206)
8. InfectedHaven (21, 723, 483)
9. Crossroads (16, 9, 136)
10. TheKeeper (3, 4, 138)
11. Terrorist (0, 595, 496)
12. InfectionBot (0, 511, 430)
13. FamilyValues (0, 6, 291)
14. UndecidedBot (0, 0, 20)
15. XenoBot (0, 0, 26)
16. Researcher (0, 0, 33)
17. Strategist (0, 0, 42)
18. TheCure (0, 0, 55)
19. Socialist (0, 0, 67)
20. TrumpBot (0, 0, 77)
21. CullBot (0, 0, 81)
22. BackStabber (0, 0, 87)
23. BlunderBot (0, 0, 104)
24. RemoveInfected (0, 0, 111)
25. PFC (0, 0, 117)
26. BioterroristBot (0, 0, 118)
27. PassiveBot (0, 0, 118)
28. Smaug (0, 0, 118)
29. WeaponOfMassDissemination (0, 0, 119)
30. AllOrNothing (0, 0, 121)
31. Obamacare (0, 0, 122)
32. DisseminationBot (0, 0, 123)
33. CureThenQuarantine (0, 0, 125)
34. Madagascar (0, 0, 129)
35. OpenAndClose (0, 0, 129)
36. ThePacifist (0, 0, 130)
37. MedicBot (0, 0, 131)
38. Medic (0, 0, 133)
39. Salt (0, 0, 134)
40. Piecemeal (0, 0, 136)
41. Graymalkin (0, 0, 137)
42. PureBot (0, 0, 140)
43. MadScienceBot (0, 0, 144)
44. BipolarBot (0, 0, 149)
45. RedCross (0, 0, 151)

Doomsday-less (200 reputation) :
100 games results : http://pasted.co/220b575b
1. FamilyValues (5708, 14, 2)
2. BlunderBot (5614, 12, 3)
3. Graymalkin (5597, 17, 4)
4. PureBot (5550, 12, 5)
5. Crossroads (5543, 11, 4)
6. Salt (5488, 24, 7)
7. CureThenQuarantine (5453, 13, 7)
8. Piecemeal (5358, 121, 23)
9. TrumpBot (5355, 12, 5)
10. CullBot (5288, 12, 9)
11. AllOrNothing (5284, 13, 10)
12. Madagascar (5060, 180, 35)
13. TheKeeper (4934, 165, 44)
14. WICKED (4714, 25, 5)
15. Strategist (2266, 25, 5)
16. BackStabber (2180, 1327, 596)
17. RemoveInfected (2021, 33, 27)
18. OpenAndClose (1945, 667, 394)
19. Triage (1773, 401, 80)
20. TheCure (1465, 46, 26)
21. Obamacare (1263, 525, 247)
22. Mooch (1103, 546, 269)
23. Israel (1102, 580, 292)
24. RedCross (1086, 1700, 727)
25. ThePacifist (1069, 636, 580)
26. Researcher (1035, 113, 37)
27. UndecidedBot (825, 219, 93)
28. PassiveBot (510, 990, 567)
29. MedicBot (411, 1474, 667)
30. Medic (392, 1690, 619)
31. Socialist (139, 63, 90)
32. XenoBot (0, 82, 170)

Thank you everyone for your participation. I hope you had as great a time designing and coding your bots as I had running the game.

Is mutation a global event, or local?

We choose three actions each turn, but can they be the same action (can I choose WWW)?

nice challenge, too bad i dont know any java (yet). Anyone here willing to give me a hand later on if im unable to do it myself? Also with that sentence structure mutation seems to be missing something.

And another question, are the % values e.g. Reduce local Lethality Rate by 4% from 10% to 6% or from 10% to 9.6%?

Must all bots be in java? If so, please add the Java tag.

@NathanMerrill Mutations are drawn once per turn and are global, i.e. the same effect applied to all players.

@Geobits You can use the same actions multiple times in the same turn

@MegaTom Submissions can be in all "supported" language. For now, I can run Java (6-8), Python (2-3), Perl, Ruby, Lua, node.js and PHP. If your language is not listed here, just provide instructions and I'll try to make it run.

@Eumal You don't have to use Java, see comment above. As for the percentage, they are added like integers so it goes from 10% to 6%.

Are rounds 0 or 1-indexed?

Can we get an Execute command that murders X amount of infected (turning them straight to dead)?  Probably not a viable method to winning, but an action that seems legitimate.  Unless that's what Quarantine does (it is unclear).

can we get better instructions for how to run the controller?

@Draco18s yes, the Quarantine command is what you are looking for.

@MegaTom Thanks, I was unsure if that is what that did or not.

How do i actually give a response? Even when i modify the finished Bots they still put the same thing every time???

To be clear: the input doesn't explicitly include the global values, but are they factored into the per-user values or not?

@Geobits I haven't had the chance to run it, but I see nothing in the code that should be restricting the minimum migration above zero. Line #262 of Game.java is relevant.

When I try to run the controller by doing java bin.controller.Game, I get
Exception in thread "main" java.lang.UnsupportedClassVersionError: bin/controller/Game : Unsupported major.minor version 52.0. My java version is 1.7.0_79. How should I run the controller?

Quick grammar note: "Sane" means "mentally stable"; the word you're (probably) looking for here is "healthy."  (Would I be correct in guessing that your first language is Spanish, where "sano" means "healthy," or something closely related?)

If a player is dead, will it continue to receive input and be able to do actions? If not, under what conditions does a player die?

Id have another nice idea for a bot, will i be allowed to create a second?

@Eumel most contests let you submit more than one entry provided they are distinct (and as separate answers).  I plan on writing a smarter bot than the one entry I submitted once I clone the java repo and can do a lot of refined testing.  The last KOTH I was involved in I think I submitted 5 of the 13 bots.

@MasonWheeler Nitpicking terminology note: Your note was a terminology or vocabulary note as there was no grammar involved ;)

I guess if you want to run the game yourself you have to read the code and figure out how it works. It's a nice idea, but the explanation is minimal.

Is there a time limit for actions? Like, is doing a bounded minimax-style search out of the question?

@muddyfish: I think you need to recompile using your version. It looks like you're trying to run a Java 8-produced Java classes.

@jmite: The OP states the time limit is one second per round

@TheNumberOne P̶l̶a̶y̶e̶r̶s̶ ̶d̶o̶n̶'̶t̶ ̶d̶i̶e̶.̶ ̶T̶h̶e̶y̶ ̶c̶o̶n̶t̶i̶n̶u̶e̶ ̶t̶o̶ ̶h̶a̶v̶e̶ ̶b̶i̶r̶t̶h̶s̶ ̶e̶a̶c̶h̶ ̶t̶u̶r̶n̶,̶ ̶a̶n̶d̶ ̶l̶i̶k̶e̶w̶i̶s̶e̶ ̶c̶o̶m̶m̶a̶n̶d̶s̶ ̶a̶r̶e̶ ̶a̶c̶c̶e̶p̶t̶e̶d̶ ̶e̶a̶c̶h̶ ̶t̶u̶r̶n̶.̶ They do die, if they have no living people left.

@Thrax The way turns are currently handled (randomized at the start, same order from then on), gives players who come later in the turn order a huge advantage, which makes the end result wildly variant. Perhaps if you either 1) randomized the turn order each round, or 2) make everyone view the same state during their turn and apply the changes to everyone simultaneously at the end of the round, then the results might be more balanced, and be based more on the quality of the submission. I tested the first option, and the results are much more consistent.

@Mwr247: How about the second option?

@MasonWheeler Close, I'm french, but the word is "sain". I'll change the terminology in the question (and in the code too, I think).

when are you gonna run the next game? The immigration as a whole seems to be a little bugged, e.g. round 6 there are 45 emigrated but only 27 immigrants

@Eumel This happens because of the flooring on percentage. If you look at the first turn, 4 healthy emigrate from each state, whereas only 3 immigrate. This means that 1 healthy is lost for each player. Do you have an idea so that the remainder of the divisions is not lost? I don't want to give an advantage to anyone, so I thought of randomizing or give it to the first states (since their order is itself randomized).

@Thrax make it a weighted random. you cant just give it to the first states or else you need to run the game many times and do some statistics in order for the game to be fair.

order the states by their rest %1 basically and drop single leftover people in the top states and remove them from the que. e.g. 3 states 4.5 3.8 2.4 ordered are 0.8 0.5 0.2 with 2 people left they go to 3.8 and 4.5

Huh? Isn't the migration rate the same for immigration and emigration? Shouldn't it be a case essentially of computing for each player a migration number m_i, putting all those migrants in a pool, shuffling, and dealing back m_i to each player? (Technically this doesn't achieve the migration rate, but in the extreme case of e.g. two players with different migration numbers it's impossible to meet the stated constraints anyway).

@PeterTaylor: I am actually also confused with that.

@PeterTaylor Let's take an example : 3 States with 100 Healthy, 0 Infected and 5% Migration Rate. First, we calculate the emigrates : 100 * 5% = 4. We have a total of 4 x 3 = 12 migrants. Now, for the immigration, the Migration Rate of each State is still 5%, but by weighting it to reach 100%, each state now have 33% (floored). This gives : 12 x 33% = 3 (floored) immigrants per state. As you can see, we have a remainder of 3 migrants. I just followed Eumel suggestion and implemented a queue to be consumed in the order of the rest of the formula for each state.

actually Peters suggestion is way better. Pick all emmigrants shuffle and hand them back out. I just noticed the great accuray on the calulation there 100 * 5% = 5 Instead of flooring you could use normal rounding.

@Eumel Right, I just noticed it too... It was actually calculated with 99 Healthy! How do you suggest shuffling?

it should still come out at 5. Create an array from the emmigrants pick randomized index from 0 to size-1 see whats in there and delete that element. Keep doing it until all elements are gone

Two questions: how many bots can I make, and what are the starting values of each variable?

just keep it reasonable, as long as the bots dont work together it should be fine. Check the game.jave file its in there. @Thrax how long does 1 game take to play out?

You're using Java. Shuffling is built into the standard library.

@Thrax There's currently an overabundance of bots that exist simply to "destroy the world". While this can be a fun challenge, it's at the point where bots actually attempting to compete can't effectively counter it anymore, and we're left with whatever bots are programmed to "Cure X 3" at the end as the winners. I'd like to suggest a rule change where in order to be considered for the KOTH, bots need to at least be capable of ending up with positive "sanes" in a 1-on-1 match up against PassiveBot? The challenge is more fun when strategies actually have an effect.

@Thrax Try running each bot 1-on-1 against PassiveBot, and then only take those who come out with positive "sanes", and pit them against each other. You'll see a huge difference. Even bots like TrumpBot are capable of running "global bad" commands without needing to be suicidal.

@Thrax would it be possible to output a tabel with the values (healthy infected and so on) at the start of each player phase?

@Thrax Not sure if this is a bug/fail on your part or mine but my bots decisions are based on the first set of values on the list and not the one with the corresponding id

Some bots (TrumpBot, FamilyValues, Crossroads, CullBot, etc) have had their code updated, but the updated code hasn't been included yet. Also, ThePacifist didn't end up with a positive healthy in the 1v1? O_o

@Mwr247 I actually updated every bot before running the games yesterday. You can follow commits on git if you're not sure bots are on their latest version. And yes, in 1v1 against ThePacifist both ended up with (0, 1, 247)

@Eumel Because of the shuffling, the input message that your bot receive is not ordered. You need to take the second value of the first split (on ;) to get your id, and then find it in the next values. And as you suggested, I added intermediate scoring in a table at the end of each round.

It seems Mooch finally doesn't have the largest population... Probably just by chance. But still, nobody can beat Mooch on total birth.

I just fixed InfectedHaven, should be working now. I forgot an import statement so it did nothing in the last 3 runs...

@thrax I was reading the code and it seems that the infection rate is in fact not a rate at all, it's just the number of people who will be infected this round. So if the infection rate is at 50 and I have 1000 healthy, next round I will end up with 950 healthy, not 500. Is this WAI ?

@Thrax I think I just found the reason that end-of-the-world bots are decimating us try-to-stay-healthy bots: bioterrorism focuses on those states with healthy people left, rather than picking a state at random (repeatably, that a state can be picked up to 4 times) and only infecting if there are healthy people left in the state. If you read the log, at the beginning try-to-stay-healthy bots are mostly near the top of the list, but as more states become fully infected, those states' positions fall faster and faster.

@Garrluk yes that is correct. That's why my bot is called CullBot, because I thought if it had nothing to do with local infected count, then the infection has to be caused by animal vectors, which are to be culled. As for the wording, "rate" is correct. Rate can mean a percentage, or alternatively, the change of a quantity over time. Here it bears the latter sense, something like "speed".

@busukxuan You are right, infection rate isn't capped to 100 so it must not be a percentage. Thank you.

@Garrluk Yes, I confirm it is not a percentage. I tried to explain it in the glossary by saying "number".

@busukxuan In fact, every "global" command is too strong for the other bots to manage, since there's no limit to how many times it may affect them in the same turn. I'm not sure I can change the rules now, though...

@thrax i understand how it works, i just thought it was already implemented in the standard bots where i copied the code from

@Thrax i created a chatroom called "chat koth pandemic", could you please come and help me out with debugging a little?

@Eumel Yes, I'm at your disposal in the chatroom : http://chat.stackexchange.com/rooms/34968/chat-koth-pandemic

If there are <30 local infected, does Quarantine overflow into healthy or does it stop when it runs out of infected?

@SnoringFrog quarantine only decreases the number of infected. It never changes the number of healthy nor dead.

Will this challenge still be alive next week? I'd love to join in if it's not too late.

@DenhamCoote Sure, you still have time. By all means, post a bot!

@Thrax I just checked the GitHub, and the FamilyValues.js you have does not match the one I have submitted (as of January 26th).

Sorry, only got around to writing a bot now - hope it's not too late to join in?

@thrax can you split off the doom bots for the next run again?

@Thrax when is the next round? BlunderBot is performing quite well locally, against the Java bots - Keen to see how it stacks up in a full game!

@DenhamCoote I'll be doing 10 runs today.

BlunderBot and EvilBot take 2nd places! I'm very surprised (and pleased!)

@thrax what classifies a bot as a doomsday bot?

@thrax Reason I ask is, all three of my bots (BlunderBot, EvilBot & BipolarBot) come out positive in 1v1 with PassiveBot, yet Evil and Bipolar don't feature in the 2nd game (without doomsday bots).  Can you confirm if that's right? Thanks :)

@DenhamCoote Evil and Bipolar both didn't come out with more than 30 Healthy in a 1v1 against PassiveBot (ran 10 games each)

Ah, it's 30+? Perhaps clarify that up in the rules? Thanks for clearing that up :)

@DenhamCoote Thanks, I added an explaination about Doomsday bots.

After many iterations of bugfixing and improving i have finally managed to make my bot great^^ @Thrax I don't think i have thanked you yet for running this. It's my first koth and you have made this an amazing challenge and really fun for all of us, so thank you very much!

Do you retest Doomsdays bots occasionally, sorry, mine basically did nothing before because of a bug that is now fixed, And it may not be a Doomsdays bot anymore.

It looks like there hasn't been an update to this for a while. I'd like to see a winner chosen. I'd recommend running, say, 100 times and taking the average results (healthy, infected, dead). Then, take the top ten or twenty of that list (the "best") and run them 100 times to see who wins among them ("the best of the best").

@quintopia Sorry, I forgot to run the games. I re-tested every bot against PassiveBot to select "Doomsday" bots. I'm currently running 100 games with everyone and 100 games without "Doomsday" bots.

You have put a bounty on the question. For who it is?

@zyabin101 I'm running the final games, the bounty is for the winner

That was a close game! Thanks again for running this great challenge, had alot of fun with it! :D

Ah, just saw that EvilBot did well! Yay! Thanks for a fun KOTH @Thrax

Congrats to the winners! I'm just happy that mine was the only bot to make top 10 on both lists :D

Family Values, Node (ES6)
// Process input
var data = process.argv[2].split(';');
var round = data.shift()*1;
var id = data.shift()*1;
var playerCount = data.length;
var local = data.find(function(v) {
  return !v.indexOf(id+'_')}
).split('_');
local = {
  sane: local[1]*1,
  infected: local[2]*1,
  dead: local[3]*1,
  infectionRate: local[4]*1,
  contagionRate: local[5]*1,
  lethalityRate: local[6]*1,
  migrationRate: local[7]*1
};

// Determine response
var response = [];
for(var i=0;i<3;i++) {
  var model = {
    M: local.infectionRate,
    E: local.contagionRate * (local.sane > 0 ? 1 : 0.5),
    I: local.lethalityRate * (round > 45 ? 0 : local.sane > 0 ? 1 : 2),
    V: (local.infectionRate/4 + local.contagionRate/2 + local.lethalityRate/2) * (round > 45 ? 0 : 1),
    C: local.infected / Math.max(local.infectionRate, 1) * (round > 48 ? round : local.infectionRate + local.contagionRate/100 * local.infected < (3 - i) * 10 ? 1 : 0),
    B: local.migrationRate * 10
  };
  var max = 'M';
  for(k in model) {
    if (model[k] > model[max] ) {
      max = k;
    } else if(model[k] == model[max]) {
      max = [max, k][Math.random()*2|0];
    }
  }
  response.push(max);

  // Refactor priorities
  if(max == 'M') {
    local.infectionRate -= 4;
  } else if(max == 'E') {
    local.contagionRate -= 8;
  } else if(max == 'I') {
    local.lethalityRate -= 4;
  } else if(max == 'V') {
    local.infectionRate -= 1;
    local.contagionRate -= 4;
    local.lethalityRate -= 2;
  } else if(max == 'C') {
    local.infected -= 10;
  } else if(max == 'B') {
    local.migrationRate -= 10;
  }
}

// Respond with actions
process.stdout.write(response.join(''));

Family Values focuses of self-preservation and defense, and only performs actions to that end. It uses a point-value system to determine the best course of action to take, and then adjusts it's own status values to better determine it's next priority. In the event of a tie, it randomly chooses from among the best options.
EDIT: Seems to be doing alright so far:
    ********** FINISH **********
    1. FamilyValues (1143, 0, 188)
    2. Triage (582, 0, 158)
    3. Researcher (281, 0, 142)
    4. Madagascar (149, 0, 162)
    5. Mooch (148, 0, 331)
    6. MedicBot (142, 0, 161)
    7. Medic (66, 65, 211)
    8. XenoBot (0, 0, 22)
    9. WMDbot (0, 0, 218)
    10. PassiveBot (0, 0, 221)
    11. BioterroristBot (0, 0, 221)
    12. MadScienceBot (0, 0, 221)
    13. DisseminationBot (0, 0, 221)
    14. TheCure (0, 0, 222)

The Pacifist, Node
// Process input
var data = process.argv[2].split(';');
var round = data.shift()*1;

// Respond with actions
process.stdout.write(round == 1 ? 'OOO' : 'PPP');

With so much focus on killing and death, the Pacifist believes that strong global health means strong local health. As such, they pretty much just focuses on reducing global illness, while leaving borders partly open to allow the goodness to spread around.

Wow, I didn't expect TheCure to be the last

@justhalf With this many players, they all end up moving around alot on the boards: just ran one now where TheCure ended up in 3rd. Both FamilyValues and Triage are almost always in the top two though, with FV getting #1 most of the time.

Hmm, the task itself is deterministic, right? So is it due to some players putting randomness into their algorithm?

@justhalf The biggest factor by far appears to be turn order, which is randomized (but the same for every round in a game). Going first means you don't have a chance to react to everyone's actions during the current turn, whereas going last allows you to best adjust to what other players have done to you.

@justhalf I just tested a modified controller that randomizes turn order for each round, and the results are much more consistent now.

Can you update your answer with that additional useful information?

@justhalf I would, but it doesn't seem relevant as part of an answer. I left a comment for OP about it, but whether or not they change it is up to them.

@Mrw247 You suggestion is valuable, I randomized the order each round.

TrumpBot
private void sleep(String[] args) {

    round = Integer.parseInt(args[0]);
    thisTownID = Integer.parseInt(args[1]);

    states = new ArrayList<>();
    //states.add(new State(args[thisTownID+2]));

    otherStates = new ArrayList<>();

    for (int i = 2; i < args.length; i++){
        states.add(new State(args[i]));
    }

    for (State state : states){
        if (state.ownerId == thisTownID) {
            thisState = state;
        } else {
            otherStates.add(state);
        }
    }

    StringBuilder cmd = new StringBuilder();

    for (int j =0;j<3;j++){
        if (thisState.infected > 7) {
          if (thisState.infected > 25){
            cmd.append("Q");
            thisState.infected -= 30;
          }
          else {
            cmd.append("C");
            thisState.infected -= 10;
          }
        }
        else if (thisState.migrationRate > 2) {
          cmd.append("B");
          thisState.migrationRate -= 10;
        }
        else if (thisState.infectionRate > 4) {
          cmd.append("M");
          thisState.infectionRate  -= 4;
        }
        else if (thisState.contagionRate > 10 || thisState.lethalityRate > 6 || thisState.infectionRate > 0) {
          cmd.append("V");
          thisState.contagionRate  -= 4;
          thisState.lethalityRate  -= 2;
          thisState.infectionRate  -= 1;
        }

        else if (thisState.infected % 10 <= 6){
          cmd.append("T");
          thisState.infected +=4;
        }
        else cmd.append("V");
    }
    System.out.print(cmd.reverse());
}

Makes America great by curing all the infected unless there are only 2 or less; minorities will be ignored.
Having less infections makes the medicine cheaper.
Needs no immigrants – they only bring infection.
If there is nothing left to do, bomb the other players.
Reversed command order to the American way, bombs first cure people later.
Edit: fixed a bug that would spam cures because the infected count wasn't lowered after curing.
Trumpscript
Thanks to J Atkin for providing it:
Make turn 4000000
As long as, turn larger than 1000000;:
If, refugee count > 2000000;: say "C"!
Else if, infectionRate > 200000;: say "M"!
Else if, immigration rate > 9000000;: say "B"!
Else: say "T"!
Make turn old turn - 1000000!
America is Great.

AllOrNothing, R
args <- strsplit(commandArgs(TRUE),";")[[1]]
round <- as.integer(args[1])
me <- as.integer(args[2])
stats <- do.call(rbind,strsplit(args[-(1:2)],"_"))
stats <- as.data.frame(apply(stats,2,as.integer))
colnames(stats) <- c("id","Sane","Infected","Dead","InfRate","ContRate","LethRate","MigRate")
out <- ""
statme <- stats[stats$id==me,]
while(nchar(out)<3){
    if(round==1 & !nchar(out)){
        out <- paste0(out, "B")
    }else if(round%%5==4 & statme$Infected > 20){
        statme$Infected <- statme$Infected - 30
        out <- paste0(out, "Q")
    }else if(statme$Sane*statme$InfRate/100 >= 1){
        o <- ifelse(statme$Sane*statme$InfRate/100 < statme$Infected*statme$ContRate/100, "C", "M")
        if(o=="C") statme$Infected <- statme$Infected - 10
        if(o=="M") statme$InfRate <- statme$InfRate - 4
        out <- paste0(out, o)
    }else if(statme$Infected > 0){
        statme$Infected <- statme$Infected - 10
        out <- paste0(out, "C")
    }else if(median(stats$LethRate)<20){
        out <- paste0(out, "W")
    }else{
        out <- paste0(out, "E")
    }
}
cat(substr(out,1,3))

Invoked by Rscript AllOrNothing.R.
The idea here is on one hand to limit to the maximum the risk of infection (by lowering infection rate, curing the infected and preventing infected to immigrate) and on the other hand to increase the lethality of the disease so that the people that do get infected, die before contaminating the others.
Edit: tweaked the strategy a little.

Nice, it seems to be doing extraordinarily good against the 21 other states!

@Thrax Indeed, though occasionally, during some runs, it still fails quite impressively :) But most often it succeeds as in your test run.

thats quite the amazing bot, good job

This looks very similar to a state I was going to write when I found the time, if not identical.  Mine only exists in my head though ("if 20ish infected, quarantine them, if 10ish, cure them, etc.") so good job.

Medic
The Medic was always... troubled, so to speak, by the people without medicine. He loves to practice medicine, so that's all he does. He also likes pythons, so he wrote his code in Python. It all makes sense, if you think about it. No, it really doesn't. Actually, it kinda does...
from random import *
commands = ""
while len(commands) < 3:
    chance = random()
    if chance < .5: commands += "V"
    elif chance < .66: commands += "I"
    elif chance < .84: commands += "E"
    else: commands += "M"

print(commands)

I am here to help.

Pythons as in the rod of Asclepius?  Or as in the staff of Hermes?  It can make some sense...

Either! Both! Why not? It all makes sense!

++1; for TF2 :D

The Cure
This seems a bit too simplistic, but it also seems like a pretty good method to keep the infection/death rate down. On every turn, outputs MCQ:

Reduce infection rate
Cure some infected
Quarantine some of the remaining infected

That's it!
public class TheCure{
    public static void main(String[]a){
        System.out.println("MCQ");
    }
}

I could probably improve this by outputting more M (or B) if I have no currently infected instead of curing and quarantining, but I'd like to see how well this does first. Unfortunately, one side effect of posting first is that it's hard to gauge effectiveness:

WICKED, Kotlin
Remember, WICKED is good.
package wicked

import java.util.*

fun clamp(value : Double, floor : Double, ceil : Double = Double.POSITIVE_INFINITY) = Math.max(Math.min(value, ceil), floor)
fun clamp(value : Int, floor : Int, ceil : Int = Integer.MAX_VALUE) = Math.max(Math.min(value, ceil), floor)

data class Player(
        val id            : Int,
        var healthy          : Int,
        var infected      : Int,
        var dead          : Int,
        var infectionRate : Int,
        var contagionRate : Double,
        var lethalityRate : Double,
        var migrationRate : Double)

class Game(val players : List<Player>) {

    fun doAction(playerId: Int, a: Char) {
        val player = players.first { it.id == playerId }
        with(player) {
            when (a) {
                'N' -> {}
                'M' -> infectionRate = clamp(infectionRate - 4, 0)
                'E' -> contagionRate = clamp(contagionRate - .08, 0.0, 1.0)
                'I' -> lethalityRate = clamp(lethalityRate - .04, 0.0, 1.0)
                'V' -> {
                    infectionRate = clamp(infectionRate - 1, 0)
                    contagionRate = clamp(contagionRate - .04, 0.0, 1.0)
                    lethalityRate = clamp(lethalityRate - .02, 0.0, 1.0)
                }
                'C' -> {
                    val cured = Math.min(infected, 10)
                    infected -= cured
                    healthy += cured
                }
                'Q' -> infected = clamp(infected - 30, 0)
                'O' -> migrationRate = clamp(migrationRate + .1, 0.0, 1.0)
                'B' -> migrationRate = clamp(migrationRate - .1, 0.0, 1.0)
                'T' -> {
                    players.forEach {
                        val infected = Math.min(it.healthy, 4)
                        it.healthy -= infected
                        it.infected += infected
                    }
                }
                'W' -> {
                    players.forEach {
                        it.infectionRate++
                        it.lethalityRate = clamp(it.lethalityRate + .02, 0.0, 1.0)
                    }
                }
                'D' -> {
                    players.forEach {
                        it.infectionRate++
                        it.contagionRate = clamp(it.contagionRate + .02, 0.0, 1.0)
                    }
                }
                'P' -> {
                    players.forEach {
                        it.infectionRate = clamp(it.infectionRate - 1, 0)
                        it.contagionRate = clamp(it.contagionRate - .01, 0.0, 1.0)
                        it.lethalityRate = clamp(it.lethalityRate - .01, 0.0, 1.0)
                    }
                }
                else -> throw IllegalArgumentException("Invalid action: $a")
            }
        }
    }

    fun copy() = Game(players.map { it.copy() })

    fun migration() {
        var migratingHealthy = 0
        var migratingInfected = 0
        var totalMigratingWeight = 0.0

        players.forEach {
            migratingHealthy += (it.healthy * it.migrationRate).toInt()
            migratingInfected += (it.infected * it.migrationRate).toInt()
            totalMigratingWeight += it.migrationRate

            it.healthy = (it.healthy * (1 - it.migrationRate)).toInt()
            it.infected *= (it.healthy * (1 - it.migrationRate)).toInt()
        }

        players.forEach {
            it.healthy += (migratingHealthy * it.migrationRate / totalMigratingWeight).toInt()
            it.infected += (migratingInfected * it.migrationRate / totalMigratingWeight).toInt()
        }
    }

    fun infection() {
        players.forEach {
            val infected = it.infectionRate //Allow negative healthy.
            it.healthy -= infected
            it.infected += infected
        }
    }

    fun contagion() {
        players.forEach {
            val infected = (it.infected * it.contagionRate).toInt()
            it.healthy -= infected
            it.infected += infected
        }
    }

    fun extinction() {
        players.forEach {
            val killed = (it.infected * it.lethalityRate).toInt()
            it.infected -= killed
            it.dead += killed
        }
    }

    operator fun get(playerId : Int) = players.first { it.id == playerId }

    fun calculateBenefit(action : Char, myId: Int) : Int {

        val copy1 = copy()
        copy1.doAction(myId, action)

        copy1.migration()
        copy1.infection()
        copy1.contagion()
        copy1.extinction()

        return copy1[myId].healthy
    }

}

fun main(args : Array<String>) {
    @Suppress("NAME_SHADOWING")
    val args = args[0].split(';')

    val round = args[0].toInt()
    val myId = args[1].toInt()

    val players : MutableList<Player> = ArrayList()

    for ( i in 2..args.size-1) {
        val data = args[i].split('_')
        players.add(Player(data[0].toInt(), data[1].toInt(), data[2].toInt(), data[3].toInt(), data[4].toInt(), data[5].toDouble() / 100, data[6].toDouble() / 100, data[7].toDouble() / 100))
    }

    val currentGame = Game(players)

    if (round == 50) {
        println("CCC")  //Extra 30 at end of game.
        return
    }

    for (i in 1..3) {
        val action = determineBestAction(currentGame, myId)
        currentGame.doAction(myId, action)
        print(action)
    }
}

fun determineBestAction(game : Game, myId : Int) : Char {

    if (game[myId].lethalityRate > .02) {        //Save the executives!!!
        return 'I'
    } else if (game[myId].lethalityRate > 0) {
        return 'V'
    }

    val bestAction = "NMEIVQCOBP".maxBy { game.calculateBenefit(it, myId) }!!

    return bestAction

}

Compile with: kotlinc WICKED.kt
Run with: kotlin wicked.WICKEDKt
PFC, Kotlin
Attempts to release the disease on everyone.
package pfc

import java.util.*

fun clamp(value : Double, floor : Double, ceil : Double = Double.POSITIVE_INFINITY) = Math.max(Math.min(value, ceil), floor)
fun clamp(value : Int, floor : Int, ceil : Int = Integer.MAX_VALUE) = Math.max(Math.min(value, ceil), floor)

data class Player(
        val id            : Int,
        var healthy          : Int,
        var infected      : Int,
        var dead          : Int,
        var infectionRate : Int,
        var contagionRate : Double,
        var lethalityRate : Double,
        var migrationRate : Double)

class Game(val players : List<Player>) {

    fun doAction(playerId: Int, a: Char) {
        val player = players.first { it.id == playerId }
        with(player) {
            when (a) {
                'N' -> {}
                'M' -> infectionRate = clamp(infectionRate - 4, 0)
                'E' -> contagionRate = clamp(contagionRate - .08, 0.0, 1.0)
                'I' -> lethalityRate = clamp(lethalityRate - .04, 0.0, 1.0)
                'V' -> {
                    infectionRate = clamp(infectionRate - 1, 0)
                    contagionRate = clamp(contagionRate - .04, 0.0, 1.0)
                    lethalityRate = clamp(lethalityRate - .02, 0.0, 1.0)
                }
                'C' -> {
                    val cured = Math.min(infected, 10)
                    infected -= cured
                    healthy += cured
                }
                'Q' -> infected = clamp(infected - 30, 0)
                'O' -> migrationRate = clamp(migrationRate + .1, 0.0, 1.0)
                'B' -> migrationRate = clamp(migrationRate - .1, 0.0, 1.0)
                'T' -> {
                    players.forEach {
                        val infected = Math.min(it.healthy, 4)
                        it.healthy -= infected
                        it.infected += infected
                    }
                }
                'W' -> {
                    players.forEach {
                        it.infectionRate++
                        it.lethalityRate = clamp(it.lethalityRate + .02, 0.0, 1.0)
                    }
                }
                'D' -> {
                    players.forEach {
                        it.infectionRate++
                        it.contagionRate = clamp(it.contagionRate + .02, 0.0, 1.0)
                    }
                }
                'P' -> {
                    players.forEach {
                        it.infectionRate = clamp(it.infectionRate - 1, 0)
                        it.contagionRate = clamp(it.contagionRate - .01, 0.0, 1.0)
                        it.lethalityRate = clamp(it.lethalityRate - .01, 0.0, 1.0)
                    }
                }
                else -> throw IllegalArgumentException("Invalid action: $a")
            }
        }
    }

    fun copy() = Game(players.map { it.copy() })

    fun migration() {
        var migratingHealthy = 0
        var migratingInfected = 0
        var totalMigratingWeight = 0.0

        players.forEach {
            migratingHealthy += (it.healthy * it.migrationRate).toInt()
            migratingInfected += (it.infected * it.migrationRate).toInt()
            totalMigratingWeight += it.migrationRate

            it.healthy = (it.healthy * (1 - it.migrationRate)).toInt()
            it.infected *= (it.healthy * (1 - it.migrationRate)).toInt()
        }

        players.forEach {
            it.healthy += (migratingHealthy * it.migrationRate / totalMigratingWeight).toInt()
            it.infected += (migratingInfected * it.migrationRate / totalMigratingWeight).toInt()
        }
    }

    fun infection() {
        players.forEach {
            val infected = Math.min(it.healthy, it.infectionRate)
            it.healthy -= infected
            it.infected += infected
        }
    }

    fun contagion() {
        players.forEach {
            val infected = Math.min(it.healthy, (it.infected * it.contagionRate).toInt())
            it.healthy -= infected
            it.infected += infected
        }
    }

    fun extinction() {
        players.forEach {
            val killed = (it.infected * it.lethalityRate).toInt()
            it.infected -= killed
            it.dead += killed
        }
    }

    operator fun get(playerId : Int) = players.first { it.id == playerId }

    fun calculateBenefit(action : Char, myId: Int) : Int {

        val copy1 = copy()
        copy1.doAction(myId, action)

        copy1.migration()
        copy1.infection()
        copy1.contagion()
        copy1.extinction()

        return copy1.players.sumBy { it.infected }
    }

}

fun main(args : Array<String>) {
    @Suppress("NAME_SHADOWING")
    val args = args[0].split(';')

    @Suppress("UNUSED_VARIABLE")
    val round = args[0].toInt()
    val myId = args[1].toInt()

    val players : MutableList<Player> = ArrayList()

    for ( i in 2..args.size-1) {
        val data = args[i].split('_')
        players.add(Player(data[0].toInt(), data[1].toInt(), data[2].toInt(), data[3].toInt(), data[4].toInt(), data[5].toDouble() / 100, data[6].toDouble() / 100, data[7].toDouble() / 100))
    }

    val currentGame = Game(players)

    for (i in 1..3) {
        val action = determineBestAction(currentGame, myId)
        currentGame.doAction(myId, action)
        print(action)
    }
}

fun determineBestAction(game : Game, myId : Int) : Char {

    val bestAction = "NMEIVCQOBTWDP".maxBy { game.calculateBenefit(it, myId) }!!

    return bestAction

}

Compile with: kotlinc PFC.kt
Run with: kotlin pfc.PFCKt
Terrorist, Kotlin
Tries to make all people dead.
package terrorist

import java.util.*

fun clamp(value : Double, floor : Double, ceil : Double = Double.POSITIVE_INFINITY) = Math.max(Math.min(value, ceil), floor)
fun clamp(value : Int, floor : Int, ceil : Int = Integer.MAX_VALUE) = Math.max(Math.min(value, ceil), floor)

data class Player(
        val id            : Int,
        var healthy          : Int,
        var infected      : Int,
        var dead          : Int,
        var infectionRate : Int,
        var contagionRate : Double,
        var lethalityRate : Double,
        var migrationRate : Double)

class Game(val players : List<Player>) {

    fun doAction(playerId: Int, a: Char) {
        val player = players.first { it.id == playerId }
        with(player) {
            when (a) {
                'N' -> {}
                'M' -> infectionRate = clamp(infectionRate - 4, 0)
                'E' -> contagionRate = clamp(contagionRate - .08, 0.0, 1.0)
                'I' -> lethalityRate = clamp(lethalityRate - .04, 0.0, 1.0)
                'V' -> {
                    infectionRate = clamp(infectionRate - 1, 0)
                    contagionRate = clamp(contagionRate - .04, 0.0, 1.0)
                    lethalityRate = clamp(lethalityRate - .02, 0.0, 1.0)
                }
                'C' -> {
                    val cured = Math.min(infected, 10)
                    infected -= cured
                    healthy += cured
                }
                'Q' -> infected = clamp(infected - 30, 0)
                'O' -> migrationRate = clamp(migrationRate + .1, 0.0, 1.0)
                'B' -> migrationRate = clamp(migrationRate - .1, 0.0, 1.0)
                'T' -> {
                    players.forEach {
                        val infected = Math.min(it.healthy, 4)
                        it.healthy -= infected
                        it.infected += infected
                    }
                }
                'W' -> {
                    players.forEach {
                        it.infectionRate++
                        it.lethalityRate = clamp(it.lethalityRate + .02, 0.0, 1.0)
                    }
                }
                'D' -> {
                    players.forEach {
                        it.infectionRate++
                        it.contagionRate = clamp(it.contagionRate + .02, 0.0, 1.0)
                    }
                }
                'P' -> {
                    players.forEach {
                        it.infectionRate = clamp(it.infectionRate - 1, 0)
                        it.contagionRate = clamp(it.contagionRate - .01, 0.0, 1.0)
                        it.lethalityRate = clamp(it.lethalityRate - .01, 0.0, 1.0)
                    }
                }
                else -> throw IllegalArgumentException("Invalid action: $a")
            }
        }
    }

    fun copy() = Game(players.map { it.copy() })

    fun migration() {
        var migratingHealthy = 0
        var migratingInfected = 0
        var totalMigratingWeight = 0.0

        players.forEach {
            migratingHealthy += (it.healthy * it.migrationRate).toInt()
            migratingInfected += (it.infected * it.migrationRate).toInt()
            totalMigratingWeight += it.migrationRate

            it.healthy = (it.healthy * (1 - it.migrationRate)).toInt()
            it.infected *= (it.healthy * (1 - it.migrationRate)).toInt()
        }

        players.forEach {
            it.healthy += (migratingHealthy * it.migrationRate / totalMigratingWeight).toInt()
            it.infected += (migratingInfected * it.migrationRate / totalMigratingWeight).toInt()
        }
    }

    fun infection() {
        players.forEach {
            val infected = Math.min(it.healthy, it.infectionRate)
            it.healthy -= infected
            it.infected += infected
        }
    }

    fun contagion() {
        players.forEach {
            val infected = Math.min(it.healthy, (it.infected * it.contagionRate).toInt())
            it.healthy -= infected
            it.infected += infected
        }
    }

    fun extinction() {
        players.forEach {
            val killed = (it.infected * it.lethalityRate).toInt()
            it.infected -= killed
            it.dead += killed
        }
    }

    operator fun get(playerId : Int) = players.first { it.id == playerId }

    fun calculateBenefit(action : Char, myId: Int) : Int {

        val copy1 = copy()
        copy1.doAction(myId, action)

        copy1.migration()
        copy1.infection()
        copy1.contagion()
        copy1.extinction()

        return copy1.players.sumBy { it.dead }
    }

}

fun main(args : Array<String>) {
    @Suppress("NAME_SHADOWING")
    val args = args[0].split(';')

    @Suppress("UNUSED_VARIABLE")
    val round = args[0].toInt()
    val myId = args[1].toInt()

    val players : MutableList<Player> = ArrayList()

    for ( i in 2..args.size-1) {
        val data = args[i].split('_')
        players.add(Player(data[0].toInt(), data[1].toInt(), data[2].toInt(), data[3].toInt(), data[4].toInt(), data[5].toDouble() / 100, data[6].toDouble() / 100, data[7].toDouble() / 100))
    }

    if (round == 50) {
        println("TTT")  //Let's mess up the scoreboard :D
        return
    }

    val currentGame = Game(players)
    for (i in 1..3) {
        val action = determineBestAction(currentGame, myId)
        currentGame.doAction(myId, action)
        print(action)
    }
}

fun determineBestAction(game : Game, myId : Int) : Char {

    if (game[myId].lethalityRate > .02) {          //We don't want to hurt ourselves.
        return 'I'
    } else if (game[myId].lethalityRate > 0) {
        return 'V'
    }

    val bestAction = "NMEIVCQOBTWDP".maxBy { game.calculateBenefit(it, myId) }!!

    return bestAction

}

Compile with: kotlinc Terrorist.kt
Run with: kotlin terrorist.TerroristKt

Compiling gives me a "wicked" folder with Game, Player, and WICKEDkt classes. Running as you described returns: "error: could not find or load main class wicked.WICKEDKt"

That's odd, it works on my computer just fine. Are you sure that the WICKEDkt class file isn't actually named WICKEDKt? Also, make sure that your working directory is not inside the wicked folder.

It is WICKEDKt. I typoed in the comment.

@Mwr247 Did you leave the generated wicked folder inside the submissions folder? Did you move the generated files from the wicked folder?

WICKED is good...damn you and your references to things I just read.

I think it could be effective to have a "Wicked Terrorist" that has an objective between the two, such as maximizing All Dead + your Sane*10.

I love that the terrorist has a fun extinction().

@J_F_B_M Actually, they all do :P

Madagascar, Java
Yep, going the Madagascar route. The first round, we BBB to close our borders. Otherwise, it gives a cure, and focuses on local vaccines.
public class Madagascar{
    public static void main(String[]args){
        Boolean bool = false;
        bool = args[0].startsWith("1;");

        if(bool) {
            System.out.println("BBB");
        }
        else {
            System.out.println("CVV");
        }
    }
}

Edit1 - I more-Madagascar'd
Edit2 - Thanks @Geobits for the startsWith reminder

if Geobits is right about migration rate starting at 5 and not being reducible to zero, the B's do absolutely nothing.

This currently doesn't look as though it would compile. if (b == true) (which should be if (b) as a matter of style) will give an error because the variable is actually called bool.

wouldnt if checking against round == 1 work a lot better than creating a file?

@TimmyD Luckily, the round is first, so not much parsing involved. Just check if the input starts with 1;

@Geobits Thanks for the startsWith() reminder. MUCH easier than splitting on ; and trying to recapture and ... Told ya I was rusty with Java.

Salt, Kotlin
This bot survives until all nasty players are dead. After that, it cures its population and repopulates the town with healthy people.
This bot has 5 steps:

Close borders.
Allow the infected to die, but not too quickly (finding exactly how fast too quickly is is the hard part).
Prevent the infected from infecting the healthy.
Cure the infected (with salt :P ).
Reproduce.

Here it is:
package salt

import java.io.File
import java.util.*

data class Player(
        val id            : Int,
        var healthy       : Int,
        var infected      : Int,
        var dead          : Int,
        var infectionRate : Int,
        var contagionRate : Int,
        var lethalityRate : Int,
        var migrationRate : Int)

fun main(args : Array<String>) {
    @Suppress("NAME_SHADOWING")
    val args = args[0].split(';')

    val round = args[0].toInt()
    val myId = args[1].toInt()

    val players : MutableList<Player> = ArrayList()

    for ( i in 2..args.size-1) {
        val data = args[i].split('_')
        players.add(Player(data[0].toInt(), data[1].toInt(), data[2].toInt(), data[3].toInt(), data[4].toInt(), data[5].toInt(), data[6].toInt(), data[7].toInt()))
    }

    if (round == 50) {
        println("CCC")  //Extra 30 at end of game.
        return
    }

    var actionsLeft = 3

    val me = players.first { it.id == myId }
    val dataFile = File("Salt.txt")
    val lastRoundInfected : Int
    var roundsInHole : Int
    if (round == 1) {
        lastRoundInfected = 1
        roundsInHole = 0
    } else {
        val lines = dataFile.readLines()
        lastRoundInfected = lines[0].toInt()
        roundsInHole = lines[1].toInt()
    }

    val wantedInfected = lastRoundInfected * Math.pow(1/1.5, 1.0/5) * (if (round % 5 == 0 && round != 0) 1.5 else 1.0)

    while (me.migrationRate > 0) {
        print('B')          //Close borders
        me.migrationRate = Math.max(0, me.migrationRate - 10)
        actionsLeft--
    }

    if (me.infected <= wantedInfected) {   //Our infected are dieing too quickly
        roundsInHole++
    } else {
        roundsInHole = Math.max(0, roundsInHole - 1)
    }

    if (me.lethalityRate > 0) {
        var lethalityRateDelta = roundsInHole * 2
        while (lethalityRateDelta > 0 && me.lethalityRate > 0 && actionsLeft > 0) {
            if (lethalityRateDelta == 2 || me.lethalityRate <= 2) {
                lethalityRateDelta -= 2
                print('V')  //Research vaccines
                me.infectionRate = Math.max(0, me.infectionRate - 1)
                me.contagionRate = Math.max(0, me.contagionRate - 4)
                me.lethalityRate = Math.max(0, me.lethalityRate - 2)
                actionsLeft--
            } else {
                lethalityRateDelta -= 4
                print('I')
                me.lethalityRate = Math.max(0, me.lethalityRate - 4)
                actionsLeft--
            }
        }
    }

    dataFile.writeText("${me.infected}\n$roundsInHole")

    while (actionsLeft > 0) {
        if (me.infectionRate + me.contagionRate * me.infected / 100 <= 0) {
            break
        }
        val mWeight = Math.min(me.infectionRate, 4)
        val eWeight = Math.min(me.contagionRate, 8) * me.infected / 100
        val vWeight = Math.min(me.contagionRate, 4) * me.infected / 100 + Math.min(me.infectionRate, 1)
        if (mWeight > eWeight && mWeight > vWeight) {
            print('M')      //Research microbiology
            me.infectionRate = Math.max(0, me.infectionRate - 4)
        } else if (eWeight > vWeight){
            print('E')      //Research epidemiology
            me.contagionRate = Math.max(0, me.contagionRate - 8)
        } else {
            print('V')      //Research vaccines
            me.infectionRate = Math.max(0, me.infectionRate - 1)
            me.contagionRate = Math.max(0, me.contagionRate - 4)
            me.lethalityRate = Math.max(0, me.lethalityRate - 2)
        }
        actionsLeft--
    }

    while (actionsLeft > 0) {
        if (me.infected <= 0) {
            break
        }
        print('C')          //Cure
        val cured = Math.min(me.infected, 10)
        me.infected -= cured
        me.healthy += cured
        actionsLeft--
    }

    while (actionsLeft > 0) {
        print('N')          //Do nothing
        actionsLeft--
    }

    return
}

Compile with: kotlinc Salt.kt
Run with: kotlin salt.SaltKt
EDIT: Higher likelihood of surviving until most of the "end the world" bots are dead.
Example results:
1. Salt (247, 12, 280)
2. InfectedTown (30, 2016, 843)
3. ZombieState (30, 1030, 609)
4. WICKED (30, 413, 222)
5. Triage (18, 965, 706)
6. Mooch (18, 657, 597)
7. MadScienceBot (18, 305, 647)
8. TheKeeper (13, 0, 158)
9. FamilyValues (10, 110, 373)
10. Madagascar (2, 0, 271)
11. Terrorist (0, 1358, 651)
12. InfectionBot (0, 1217, 830)
13. Medic (0, 27, 340)
14. MedicBot (0, 1, 200)
15. UndecidedBot (0, 0, 33)
16. Researcher (0, 0, 63)
17. TheCure (0, 0, 71)
18. TrumpBot (0, 0, 88)
19. WeaponOfMassDissemination (0, 0, 137)
20. Strategist (0, 0, 142)
21. PassiveBot (0, 0, 149)
22. DisseminationBot (0, 0, 152)
23. PassiveBot (0, 0, 155)
24. Crossroads (0, 0, 164)
25. InfectedHaven (0, 0, 170)
26. Socialist (0, 0, 172)
27. BioterroristBot (0, 0, 175)
28. XenoBot (0, 0, 184)
29. ThePacifist (0, 0, 199)
30. CullBot (0, 0, 294)
31. AllOrNothing (0, 0, 327)

Very nice strategy. I like that you make use of lethality in a "good" way!

Good job putting an end to the "end-of-the-world" bots! However, Salt might topple if there were a few more of those bots, I'm not sure though. The ability to survive that is very impressive already, nice one man!

@busukxuan As it currently is, several bots would have to maximize the lethality rate before they could kill this, killing themselves in the process.

Infected Town, Java
Infected town doesn't care if people are infested as long as they don't die.
It's why it will decrease the local lethality rate as much as possible.
When the lethality rate is already very low, it use its remaining actions to increase the global lethality rate before decreasing it's own.
Since it tries to be the biggest town around, the immigration balance can only be negative, so its first action is to close the borders.
During the last turn, the lethality rate has no effect and the ranking is done on the number of sane people in the town, so it cures 30 people and hopes that it will be enough.
import java.util.ArrayList;
import java.util.List;

public class InfectedTown {

    int playerID;
    State thisState;

    public static void main(String[] args){
        new InfectedTown().sleep(args[0].split(";"));
    }

    private void sleep(String[] args) {
        // Parse arguments
        int round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        for (int i = 2; i < args.length; i++){
            thisState = new State(args[i]);
            if(thisState.isMine()){
                break;
            }
        }

        // Special actions on turn 1 and 50.
        String action="";
        if(round == 1){
            action = "B";
        } else if(round == 50){
            action="CCC";
        }

        while(action.length()<3){
            if(thisState.lethalityRate<=2 && action.length()<2){
                // We still have at least one action: lets increase the
                // lethality rate for everyone, we will decrease it with our
                // remaining actions.
                action+="W";
                thisState.lethalityRate+=2;
            } else if (thisState.lethalityRate>=4
                    ||(thisState.lethalityRate>0 && action.length()==2)) {
                // Don't let people die!
                action+="I";
                thisState.lethalityRate-=4;
            } else {
                // Nothing better to do, lets distract other towns by
                // increasing some useless values
                action+="D";
            }
        }

       System.out.println(action);
    }

    private class State {
        public int ownerId;
        public int lethalityRate;

        public State(String string) {
            String[] args = string.split("_");
            ownerId = Integer.parseInt(args[0]);
            lethalityRate = Integer.parseInt(args[6]);
        }

        public boolean isMine(){
            return ownerId == playerID;
        }
    }
}

PureBot (Haskell)
PureBot hates one thing: Side effects!
It will try to handle all side effects, and if everything goes well, it will reduce the amount of side effects produced by the outside world.
It also ignores all side effects in its calculations.
This makes it play significantly better against passive enemies (that do not change global rates).
If infected, infection, contagion, lethality and migration are all zero, it will help the other bots with the P (for Pure) command.
module Main where
import Control.Monad (void)
import Data.List (find)
import System.Environment (getArgs)
import System.Exit (exitFailure)
import Text.Parsec

-- | The world
data World = World
    { worldRound  :: Int    -- ^ The current round
    , worldTownID :: Int    -- ^ The current town ID
    , worldTowns  :: [Town] -- ^ List of all towns in the world
    }
    deriving (Show)

-- | A town in the world
data Town = Town
    { townID            :: Int -- ^ The town ID
    , townDeath         :: Int -- ^ The number of death people in the town
    , townHealthy       :: Int -- ^ The number of healthy people in the town
    , townInfected      :: Int -- ^ The number of infected people in the town
    , townInfectionRate :: Int -- ^ The infaction rate of the town
    , townContagionRate :: Int -- ^ The contagion rate of the town
    , townLethalityRate :: Int -- ^ The lethality rate of the town
    , townMigrationRate :: Int -- ^ The migration rate of the town
    }
    deriving (Show)

-- | Parse a Int
parseInt :: Parsec String () Int
parseInt = do
    sign <- option '+' $ oneOf "+-"
    numb <- read <$> many1 digit
    return $ if sign == '+'
        then numb
        else negate numb

-- | Parse a town
parseTown :: Parsec String () Town
parseTown = do
    nID <- parseInt
    void $ char '_'
    nHealthy <- parseInt
    void $ char '_'
    nInfected <- parseInt
    void $ char '_'
    nDeath <- parseInt
    void $ char '_'
    nInfectionRate <- parseInt
    void $ char '_'
    nContagionRate <- parseInt
    void $ char '_'
    nLethalityRate <- parseInt
    void $ char '_'
    nMigrationRate <- parseInt
    return Town
        { townID            = nID
        , townDeath         = nDeath
        , townHealthy       = nHealthy
        , townInfected      = nInfected
        , townInfectionRate = nInfectionRate
        , townContagionRate = nContagionRate
        , townLethalityRate = nLethalityRate
        , townMigrationRate = nMigrationRate }

-- | Parse a world
parseWorld :: Parsec String () World
parseWorld = do
    nRound <- parseInt
    void $ char ';'
    nTownID <- parseInt
    void $ char ';'
    towns <- parseTown `sepBy` char ';'
    let nTowns = length towns
    if nTowns < nTownID
        then let nExpected   = (nTownID - nTowns) in
            fail $ "expected at least " ++ show nExpected ++ " more town(s)"
        else return World
            { worldRound  = nRound
            , worldTownID = nTownID
            , worldTowns  = towns }

-- | Update a town
updateTown :: World -> Town -> String
updateTown world town = take 3 $ lastRound
                   ++ prepareForReproduction
                   ++ decreaseInfected
                   ++ decreaseMigration
                   ++ decreaseInfection
                   ++ decreaseContagion
                   ++ decreaseLethality
                   ++ decreaseWorldWide
  where
    -- | The current round number
    nRound         = worldRound world
    -- | The current number of infected
    nInfected      = townInfected town
    -- | The current lethality rate
    nLethalityRate = townLethalityRate town
    -- | The current migration rate
    nMigrationRate = townMigrationRate town
    -- | The current infection rate
    nInfectionRate = townInfectionRate town
    -- | The current contagion rate
    nContagionRate = townContagionRate town
    -- | What to do on the last round
    lastRound
        | nRound == 50 = "CCC"
        | otherwise    = ""
    -- | What to do in order to prepare for reproduction
    prepareForReproduction
        | (nRound+1) `mod` 5 == 0 = decreaseInfected
        | otherwise               = ""
    -- | What to do in order to decrease infected
    decreaseInfected
        | nInfected > 25 = "CCC"
        | nInfected > 15 = "CC"
        | nInfected > 5  = "C"
        | otherwise      = ""
    -- | What to do in order to decrease lethality
    decreaseLethality
        | nLethalityRate > 4 = "I"
        | otherwise          = ""
    -- | What to do in order to decrease migration
    decreaseMigration
        | nMigrationRate > 0 = "B"
        | otherwise          = ""
    -- | What to do in order to decrease infection
    decreaseInfection
        | nInfectionRate > 0 = "M"
        | otherwise          = ""
    -- | What to do in order to decrease contagion
    decreaseContagion
        | nContagionRate > 4 = "E"
        | otherwise          = ""
    -- | What to do if everything else has been taken care of
    decreaseWorldWide = "PPP"

-- | Update a world
updateWorld :: World -> Maybe String
updateWorld world = updateTown world <$> town
  where
    town          = find ((==worldTownID world) . townID) (worldTowns world)

-- | Main program entry point
main :: IO ()
main = do
    cmds <- concat <$> getArgs
    case parse parseWorld "stdin" cmds of
        Left err    -> print err >> exitFailure
        Right world -> case updateWorld world of
            Just cmd -> putStrLn cmd
            Nothing  -> putStrLn "Failed to update world!" >> exitFailure

run with: runhaskell PureBot.hs

Wow, what technical purity! I like how you interpret the P command.

I just installed Haskell 7.10.3, and when I try to run your bot, it waits indefinitely. I tried with this : runhaskell.exe PureBot.hs 1;0;0_97_3_0_2_5_15_5;1_97_3_0_2_5_15_5. Is there anything more I need to do before running it?

@Thrax Oh, sorry. I thought you passed the commands through stdin... If it doesn't I'll change the program.

The commands are passed as arguments. I'll do next run after you have updated your bot.

@Thrax In that case it should work now.

Weapon of Mass Dissemination
public class WMDbot{
    public static void main(String[]a){
        System.out.println("WMD");
    }
}

The WMD bot is a jerk: keeps its own infection rate low and raises everyone else's.
Bot constructed purely for the acronym, likely not a strong contender, but will make the competitive field a little more...interesting.  Code borrowed from TheCure and just altered its action string.

Technically you've also altered the class name ;P

@DenhamCoote: Please don't go around mass-adding language tags. If you're going to edit a post, make sure it's something more significant (grammar, formatting, etc). Thanks.

Oh? I once had that as the only edit to one of my posts - I was just following someone else's lead...

@DenhamCoote It's one of those "easy cheap edits" that adds nothing, but which aren't actually forbidden.  Doing a bunch of them gets spammy.

Not sure what you mean by 'cheap' - It adds to the readability, for me...  This challenge has a lot more code to read than a normal golfing challenge, and I therefore thought the syntax highlighting would be welcome.  It's not like I'm doing this for points, I just happened to have enjoyed this challenge.

@DenhamCoote "Cheap" as in "easy to add."  You can pretty much copy-paste the edit into a dozen posts very quickly.

Indeed - I made 10 posts more readable in 2 minutes! I think that has value ;-)

Triage, Java
import java.util.ArrayList;
import java.util.List;

public class Triage {

    int round;
    int phase;
    int playerID;

    List<State> states;
    List<State> otherStates;

    State thisState;

    public static void main(String[] args){
        new Triage().sleep(args[0].split(";"));
    }

    private void sleep(String[] args) {

        round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        states = new ArrayList<>();
        otherStates = new ArrayList<>();

        for (int i = 2; i < args.length; i++){
            states.add(new State(args[i]));
        }

        for (State state : states){
            if (state.isMine()) {
                thisState = state;
            } else {
                otherStates.add(state);
            }
        }

        if (round == 50) {
          System.out.println("CCC");
          return;
        }

        String output = "";

        while( thisState.lethalityRate >= 4) {
          output += "I";
          thisState.lethalityRate -= 4;
        }

        while( thisState.lethalityRate > 0) {
          output += "V";
          thisState.lethalityRate -= 2;
          thisState.contagionRate -= 4;
          thisState.infectionRate -= 1;
        }

        while( thisState.contagionRate >= 8) {
          output += "E";
          thisState.contagionRate -= 8;
        }

        while( thisState.contagionRate > 0) {
          output += "V";
          thisState.lethalityRate -= 2;
          thisState.contagionRate -= 4;
          thisState.infectionRate -= 1;
        }

        while( thisState.infectionRate > 0) {
          output += "M";
          thisState.infectionRate -= 4;
        }

        while( output.length() < 3) {
          output += "C";
        }

        System.out.println(output.substring(0,3));

    }

    private class State {

        private final int ownerId;
        public int sane;
        public int infected;
        public int dead;
        public int infectionRate;
        public int contagionRate;
        public int lethalityRate;
        public int migrationRate;

        public State(String string) {
            String[] args = string.split("_");
            ownerId = Integer.parseInt(args[0]);
            sane = Integer.parseInt(args[1]);
            infected = Integer.parseInt(args[2]);
            dead = Integer.parseInt(args[3]);
            infectionRate = Integer.parseInt(args[4]);
            contagionRate = Integer.parseInt(args[5]);
            lethalityRate = Integer.parseInt(args[6]);
            migrationRate = Integer.parseInt(args[7]);
        }

        public int getOwnerId() {
            return ownerId;
        }

        public boolean isMine(){
            return getOwnerId() == playerID;
        }

    }

}

First keeps its citizens alive, then keeps them from infecting others, then cures them.
Mooch, Java
import java.util.ArrayList;
import java.util.List;

public class Mooch {

    int round;
    int phase;
    int playerID;

    List<State> states;
    List<State> otherStates;

    State thisState;

    public static void main(String[] args){
        new Mooch().sleep(args[0].split(";"));
    }

    private void sleep(String[] args) {

        round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        states = new ArrayList<>();
        otherStates = new ArrayList<>();

        for (int i = 2; i < args.length; i++){
            states.add(new State(args[i]));
        }

        for (State state : states){
            if (state.isMine()) {
                thisState = state;
            } else {
                otherStates.add(state);
            }
        }

        if (round == 50) {
          System.out.println("CCC");
          return;
        }

        String output = "";

        while( thisState.migrationRate < 100) {
          output += "O";
          thisState.migrationRate += 10;
        }

        while( thisState.lethalityRate >= 4) {
          output += "I";
          thisState.lethalityRate -= 4;
        }

        while( thisState.lethalityRate > 0) {
          output += "V";
          thisState.lethalityRate -= 2;
          thisState.contagionRate -= 4;
          thisState.infectionRate -= 1;
        }

        while( thisState.contagionRate >= 8) {
          output += "E";
          thisState.contagionRate -= 8;
        }

        while( thisState.contagionRate > 0) {
          output += "V";
          thisState.lethalityRate -= 2;
          thisState.contagionRate -= 4;
          thisState.infectionRate -= 1;
        }

        while( thisState.infectionRate > 0) {
          output += "M";
          thisState.infectionRate -= 4;
        }

        while( output.length() < 3) {
          output += "C";
        }

        System.out.println(output.substring(0,3));

    }

    private class State {

        private final int ownerId;
        public int sane;
        public int infected;
        public int dead;
        public int infectionRate;
        public int contagionRate;
        public int lethalityRate;
        public int migrationRate;

        public State(String string) {
            String[] args = string.split("_");
            ownerId = Integer.parseInt(args[0]);
            sane = Integer.parseInt(args[1]);
            infected = Integer.parseInt(args[2]);
            dead = Integer.parseInt(args[3]);
            infectionRate = Integer.parseInt(args[4]);
            contagionRate = Integer.parseInt(args[5]);
            lethalityRate = Integer.parseInt(args[6]);
            migrationRate = Integer.parseInt(args[7]);
        }

        public int getOwnerId() {
            return ownerId;
        }

        public boolean isMine(){
            return getOwnerId() == playerID;
        }

    }

}

The same as Triage, except it starts off by completely opening its borders. This ensures a giant perpetual population of infected, inconveniencing the other bots and potentially serving as a tie-breaker.

Triage keeps giving Family Values a run for it's money. Mooch, on the other hand, keeps building up a scary large army of sick people. Well done!

isnt the moochbot trying to keep everyone uninfected? Doesn´t thisState.x modify the State value and then the game changes it again from the string giving you double the effect? Great idea on thr CCC in the last round though

Understood the code now and found my second question to be pretty stupid

Moochbot does eventually try to create a healthy population, but open borders+zero lethality ensures that the infected population will grow much faster.

CullBot, Python 3
Pretty much the standard self-protection bot that closes borders and tries to lower infection rate in the town. It does so by culling animal vectors (since infected people have no effect on infection rate it must have something to do with non-human vectors; basically this is "Research microbiology"). Occasionally it "culls" infected humans too... You know, AI makes mistakes too...
# Parsing code
from sys import argv

args = argv[1].split(";")

n = int(args[0])
pid = int(args[1])
dic = ["pid","healthy","infected","dead","infection","contagion","lethality","migration"]
players = []
for p in args[2:]:
    players += [{dic[i]:int(p.split("_")[i]) for i in range(len(p.split("_")))}]
    if int(p.split("_")[0]) == pid:
        me = players[-1]

# Bot code

actions = ""
nextInfected = me["infected"]*me["contagion"]/100 + me["infection"] + me["infected"] - me["infected"]*me["lethality"]/100
if n%5 == 4:
    nextInfected *= 1.5

if n == 1:
    actions += "BM"
    if nextInfected*1.3 > 10:
        actions += "C"
    elif me["infection"] > 6:
        actions += "M"
    elif me["infection"] > 4:
        actions += "V"
    else:
        actions += "E"
    print(actions)
    exit()
elif n == 50:
    print("CCC")
    exit()

if nextInfected*1.2 > 30:
    if me["infected"] >= 23:
        actions += "Q"
        me["infected"] -= 30
    else:
        actions += "C"
        me["infected"] -= 10
elif me["infection"] > 0:
    actions += "M"
    me["infection"] -= 4
elif me["contagion"] >= 6:
    actions += "E"
    me["contagion"] -= 8
elif me["infected"] > 0:
    actions += "C"
    me["infected"] -= 10
else:
    actions += "E"
    me["contagion"] -= 8

if me["infection"] >= 3:
    actions += "M"
    me["infection"] -= 4
elif me["infected"] >= 7 :
    actions += "C"
    me["infected"] -= 10
elif me["infection"] > 0 and me["contagion"] >= 3:
    actions += "V"
    me["infection"] -= 1
    me["contagion"] -= 4
elif me["contagion"] >= 6:
    actions += "E"
    me["contagion"] -= 8
elif me["infection"] > 0:
    actions += "M"
    me["infection"] -= 4
elif me["infected"] > 0:
    actions += "C"
    me["infected"] -= 10
else:
    actions += "E"
    me["contagion"] -= 8

if me["infection"] >= 3:
    actions += "M"
    me["infection"] -= 4
elif me["infected"] >= 7 :
    actions += "C"
    me["infected"] -= 10
elif me["infection"] > 0 and me["contagion"] >= 3:
    actions += "V"
    me["infection"] -= 1
    me["contagion"] -= 4
elif me["contagion"] >= 6:
    actions += "E"
    me["contagion"] -= 8
elif me["infection"] > 0:
    actions += "M"
    me["infection"] -= 4
elif me["infected"] > 0:
    actions += "C"
    me["infected"] -= 10
else:
    actions += "E"
    me["contagion"] -= 8

if actions[-2:] == "VV":
    actions = actions[0] + "ME"
print(actions)

I'm not a Python guy, so I could be doing this wrong, but running this in Python 3.4 gives me "NameError: name 'dictionary' is not defined" on line 9.

@Mwr247 Thanks, turns out I was totally out of my mind when I wrote the parsing code... There were a lot more problems than that.

Now it gives "TypeError: 'list' object cannot be interpreted as an integer" on line 11

Now it works =)

@Mwr247 Lol! I thought I fixed that, but I didn't copy and paste the new code so I guess I missed that one. I tested, now it should be running fine. Unless I misunderstood the I/O rules somehow.

EvilBot, Java
EvilBot doesn't care about curing people. As long as they stay alive (kinda).
Tries to make the rest of the world sick.
In my local testing, BlunderBot was doing much better until I also introduced EvilBot. Seems to shake things up a bit.
import java.util.ArrayList;
import java.util.List;

public class EvilBot {

int round;
int phase;
int playerID;
int thisTownID;

List<State> states;
List<State> otherStates;

State thisState;
String action = "";
int cc=0; // command count

public static void main(String[] args){
    new EvilBot().sleep(args[0].split(";"));
}

private void action(String newAction) {
    action += newAction;
    cc+= newAction.length();
    if (cc>=3) {
        System.out.println(action.substring(0, 3));
        System.exit(0);;
    }
}
private void sleep(String[] args) {

    round = Integer.parseInt(args[0]);
    thisTownID = Integer.parseInt(args[1]);

    states = new ArrayList<>();
    otherStates = new ArrayList<>();

    for (int i = 2; i < args.length; i++){
        states.add(new State(args[i]));
    }

    for (State state : states){
        if (state.isMine()) {
            thisState = state;
        } else {
            otherStates.add(state);
        }
    }

    // Round specific commands
    if (round == 1 )                                { action("B");   }
    if (round == 50)                                { action("CCC"); }

    for (int i=0;i<3;i++){
        if (thisState.getLethalityRate() >= 4)  { action("I"); thisState.lethalityRate -= 4;}
    }

    // Nothing else to do, cause trouble.
    action("DWT");
}

private class State {

    private final int ownerId;
    private int healthy;
    private int infected;
    private int dead;
    private int infectionRate;
    private int contagionRate;
    private int lethalityRate;
    private int migrationRate;

    public State(String string) {
        String[] args = string.split("_");
        ownerId = Integer.parseInt(args[0]);
        healthy = Integer.parseInt(args[1]);
        infected = Integer.parseInt(args[2]);
        dead = Integer.parseInt(args[3]);
        infectionRate = Integer.parseInt(args[4]);
        contagionRate = Integer.parseInt(args[5]);
        lethalityRate = Integer.parseInt(args[6]);
        migrationRate = Integer.parseInt(args[7]);
    }

    public int getOwnerId() {
        return ownerId;
    }

    public int getHealthy() {
        return healthy;
    }

    public int getInfected() {
        return infected;
    }

    public int getDead() {
        return dead;
    }

    public int getInfectionRate() {
        return infectionRate;
    }

    public int getContagionRate() {
        return contagionRate;
    }

    public int getLethalityRate() {
        return lethalityRate;
    }

    public int getMigrationRate() {
        return migrationRate;
    }

    public boolean isMine(){
        return getOwnerId() == thisTownID;
    }

}

}

InfectedHaven, Python 3
A safehaven for the infected with closed borders. Tries to minimize lethality. If minimized, tries to increase lethality in other states to "benefit" the local infected.
# parsing code
from sys import argv
args = argv[1].split(";")

n = int(args[0])
pid = int(args[1])
dic = ["pid","healthy","infected","dead","infection","contagion","lethality","migration"]
players = []
for p in args[2:]:
    players += [{dic[i]:int(p.split("_")[i]) for i in range(len(p.split("_")))}]
    if int(p.split("_")[0]) == pid:
        me = players[-1]

# bot code

actions =""

if n == 50:
    print("CCC")
    exit()
elif n == 1:
    actions += "B"
    if me["lethality"] <= 6:
        actions += "WI"
    else:
        actions += "II"
    print(actions)
    exit()

if me["lethality"] >= 9:
    actions += "III"
elif me["lethality"] >= 3:
    actions += "WII"
else:
    actions += "WWI"
print(actions)

The modification you made will be taken in account for the next run. It will still not compete in the second run, since your strategy in to made a Safe Haven for Infected. Very interesting nonetheless!

@Thrax Lol thanks, it's not really a strategy. I just thought those infected will likely be prejudiced or discriminated by the healthy, so in a chaotic world this would likely catalyze a formation of a "for fellow infected" state. To be honest this was a just-for-fun bot, but then I saw a little potential in it so I submitted.

@Thrax sorry if this is inconvenient, but I just found out I miscalculated the conditions at the bottom so I updated again.

Graymalkin, Java
Graymalkin's primary focus is reducing the infection rate to 0 and growing its healthy population. It doesn't believe in quarantines... except from the outside world of course.
My first post - critique welcome. :)

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

public class Graymalkin {

    int round;
    int phase;
    int playerID;
    int thisTownID;

    List<State> states;
    List<State> otherStates;

    State thisState;

    public static void main(String[] args) {
        new Graymalkin().sleep(args[0].split(";"));
    }

    private void sleep(String[] args) {

        round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        states = new ArrayList<>();
        otherStates = new ArrayList<>();

        for (int i = 2; i < args.length; i++) {
            states.add(new State(args[i]));
        }

        for (State state : states) {
            if (state.isMine()) {
                thisState = state;
            } else {
                otherStates.add(state);
            }
        }

        if (round == 50) {
            System.out.println("CCC");
            return;
        }

        String out = "";

        if (round == 1) {
            out += "B";
        }

        if (thisState.infectionRate < 10 && thisState.infected >= 10) {
            out += "C";
            thisState.infected -= 10;
        }

        while (thisState.infectionRate >= 4) {
            out += "M";
            thisState.infectionRate -= 4;
        }

        while (thisState.infectionRate > 0) {
            out += "V";
            thisState.infectionRate -= 1;
        }

        while (out.length() < 3) {
            if (thisState.infected > 0) {
                out += "C";
                thisState.infected -= 10;
            } else if (thisState.contagionRate > 0) {
                out += "E";
                thisState.contagionRate -= 8;
            } else if (thisState.lethalityRate > 0) {
                out += "I";
                thisState.lethalityRate -= 4;
            } else {
                out += "N";
            }
        }

        System.out.println(out.substring(0, 3));
    }

    private class State {

        private final int ownerId;
        private int sane;
        private int infected;
        private int dead;
        private int infectionRate;
        private int contagionRate;
        private int lethalityRate;
        private int migrationRate;

        public State(String string) {
            String[] args = string.split("_");
            ownerId = Integer.parseInt(args[0]);
            sane = Integer.parseInt(args[1]);
            infected = Integer.parseInt(args[2]);
            dead = Integer.parseInt(args[3]);
            infectionRate = Integer.parseInt(args[4]);
            contagionRate = Integer.parseInt(args[5]);
            lethalityRate = Integer.parseInt(args[6]);
            migrationRate = Integer.parseInt(args[7]);
        }

        public int getOwnerId() {
            return ownerId;
        }

        public int getSane() {
            return sane;
        }

        public int getInfected() {
            return infected;
        }

        public int getDead() {
            return dead;
        }

        public int getInfectionRate() {
            return infectionRate;
        }

        public int getContagionRate() {
            return contagionRate;
        }

        public int getLethalityRate() {
            return lethalityRate;
        }

        public int getMigrationRate() {
            return migrationRate;
        }

        public boolean isMine() {
            return getOwnerId() == playerID;
        }
    }
}

@Thrax it looks like my bot wasn't included in your most recent run. I'm curious to see how it does!

Crossroads, Python2
Crossroads is a democratic nation with a focus on futuristic scientific values. Like most democracies, most decisions are made by unscientifically-trained, selfish, and dysfunctional committees, which frequently make very strange and poor--apparently random, even--decisions. However, the government is ultimately working for the common good of its people and the human race.
import sys
import random
import itertools
def sample_wr(population, k):
    "Chooses k random elements (with replacement) from a population"
    n = len(population)
    _random, _int = random.random, int  # speed hack
    return [population[_int(_random() * n)] for i in itertools.repeat(None, k)]
a = sys.argv[1].split(';')
round = int(a[0])
myid = a[1]
players = {}
Sane = 0
Infected = 1
Dead = 2
InfectionRate = 3
ContagionRate = 4
LethalityRate = 5
MigrationRate = 6
worldpopulation = 0
for i in range(2,len(a)):
    b = a[i].split('_')
    players[b[0]]=map(int,b[1:])
    worldpopulation += (int(b[1])+int(b[2]))*int(b[7])/100
output = ""
if round == 1:
    output="BM"
    if players[myid][Infected]>6: output+="C"
    else: output+="E"
if round == 50:
    if players[myid][Infected] > 20: output = "CCC"
    elif players[myid][Infected]> 6: output = "CTC"
    else: output = "TTC"
if round == 48 and players[myid][Infected] > 45 and players[myid][InfectionRate]>12:
    output = "MMM"
if round == 49 and players[myid][Infected] > 30:
    output = "CCC"
if (round+1)%5==0:
    if players[myid][Sane]==0 or players[myid][Infected]/players[myid][Sane] > 2: output+="I"*(players[myid][LethalityRate]/4)
    output+="M"*(players[myid][InfectionRate]/4)
    output+="C"*max((players[myid][Infected]/10),1)
if players[myid][InfectionRate] < 8 and players[myid][ContagionRate] < 20 and players[myid][Sane]+min(players[myid][Infected]/5,60)>players[myid][Infected] and (round+2)%5==0:
    output+="C"*max((players[myid][Infected]/10),1)
    players[myid][Infected] -= min(max((players[myid][Infected]/10),1)*10,players[myid][Infected])
if players[myid][Sane] > players[myid][Infected] > 30:
    output +="Q"
    players[myid][Infected] -= min(players[myid][Infected],30)
if players[myid][Sane] > players[myid][Infected] > 20:
    output+="CC"
    players[myid][Infected] -= min(players[myid][Infected],20)
if (players[myid][Sane] > 2*players[myid][Infected] > 20):
    output+="C"
    players[myid][Infected] -= min(players[myid][Infected],10)
if round <= 5 and players[myid][Infected] > 10:
    output+="C"
    players[myid][Infected] -= min(players[myid][Infected],10)
if 25 <= players[myid][Infected] < 40 and players[myid][InfectionRate]<10:# and players[myid][ContagionRate]*(players[myid][Infected]-20)/100 < 10:
    output+="CCC"

if players[myid][InfectionRate]-players[myid][ContagionRate]>10: output+="M"
if players[myid][ContagionRate]-players[myid][InfectionRate]>20: output+="E"
population = []
population +=["I" for i in range(int(1.15**players[myid][LethalityRate]))]
if players[myid][Sane]<10 or players[myid][Infected]-players[myid][Sane]>10: population+=["I" if players[myid][LethalityRate]>8 else "V" for i in range(players[myid][InfectionRate])]
if players[myid][Sane]+players[myid][Infected]>10 and (players[myid][Sane]>15 or players[myid][LethalityRate]<10): population += ["M" if players[myid][InfectionRate] > 6 else "V" for i in range(2*max(players[myid][InfectionRate]*players[myid][Sane]/100,int((1.15+0.002*(50-round))**min(50,players[myid][InfectionRate]))))]
if players[myid][Sane]+players[myid][Infected]>10 and (players[myid][Sane]>15 or players[myid][LethalityRate]<10): population += ["E" if players[myid][ContagionRate] > 10 else "V" for i in range(max(min(players[myid][Sane],players[myid][ContagionRate]*players[myid][Infected]/100),int(1.15**min(50,players[myid][ContagionRate]))))]
if players[myid][InfectionRate]+players[myid][ContagionRate]<15: population += ["C" for i in range(players[myid][Infected])]
if players[myid][Infected] < 10: population += ["WV" for i in range(int(1.05**round))]
output += ''.join(sample_wr(population,3))
print output[:3]

4 runs involving everyone:
1. Crossroads (36, 12, 185)
2. InfectedTown (14, 1040, 510)
3. InfectedHaven (14, 977, 481)
4. Triage (14, 668, 531)
5. ZombieState (14, 523, 393)

1. AllOrNothing (541, 0, 312)
2. InfectedTown (30, 1125, 574)
3. InfectedHaven (30, 1020, 612)
4. WICKED (30, 732, 622)
5. Triage (30, 553, 554)
6. Mooch (30, 80, 240)
7. Crossroads (25, 0, 162)

1. AllOrNothing (846, 12, 241)
2. Crossroads (440, 15, 146)
3. FamilyValues (388, 34, 201)
4. Salt (170, 0, 176)
5. InfectedHaven (18, 1290, 664)

1. Crossroads (80, 14, 365)
2. InfectedHaven (30, 1596, 603)
3. InfectedTown (30, 1286, 576)
4. Triage (30, 1084, 412)
5. WICKED (18, 1286, 578)

4 runs without "doomsday bots":
1. Salt (6790, 0, 58)
2. FamilyValues (6697, 7, 9)
3. Crossroads (6616, 4, 16)
4. PureBot (6454, 0, 50)
5. Piecemeal (6260, 0, 111)

1. Crossroads (6970, 0, 39)
2. PureBot (6642, 0, 77)
3. CureThenQuarantine (6350, 2, 51)
4. FamilyValues (6153, 13, 21)
5. Piecemeal (5964, 4, 132)

1. PureBot (6142, 0, 34)
2. CureThenQuarantine (6010, 4, 75)
3. Piecemeal (5971, 4, 72)
4. CullBot (5409, 8, 115)
5. Crossroads (5129, 0, 27)

1. FamilyValues (7277, 12, 26)
2. Crossroads (6544, 4, 32)
3. Salt (5830, 26, 103)
4. Piecemeal (5757, 8, 164)
5. PureBot (5657, 8, 127)

EDIT: Having seen CullBot's successful "ignore lethality and focus on keeping folks healthy" strategy, I have increased the priority of reducing infection and contagion and curing over reducing lethality, while not abandoning the essential decision-by-committee random flair.
EDIT2: It turns out ignoring lethality with lots of terrorists around is bad. priority for lethality reduction increased again, now scaling with the lethality rate. Also fixed some other poor decisions, like opening and closing borders on the same turn, and increased the threshold for quarantine, preferring to cure instead where possible.
EDIT3: A couple of minor priority adjustments to handle situations that weren't being handled. Now it scores near the top whether doomsdays are included or not, thought Salt beats it in both cases. My vote is currently with Salt for winner of this thing.
EDIT4: Improved timing and efficacity of curing.
EDIT5: Removed the stuff that messes with migration, since it never hits zero population anymore, and some more special cases for curing.
EDIT6: Increase priority of lowering infection rate in early game. Remove commented lines. I did not update the results of test runs, but it now scores considerably higher in non-doomsday runs (beating out FamilyValues, but not TrumpBot)
EDIT7: Cap infection/contagion rate exponent at 50 to prevent high memory usage.

Wait... Is Israel modeled after the WWZ Jerusalem with very tall walls?

@busukxuan that was the inspiration yes, except that this Israel more resembles the Jerusalem in the movie (rapidly overrun by infected), since it basically opens it borders wide a la Mooch. The original plan was for it to mostly do 'P', but it was not an effective strategy.

Hm... It seems dear CullBot couldn't keep up anymore when there are more bots trying to increase infection.

it does great when i added in a bot to drive down infection (up to turn order randomness)

So basically CullBot is pretty unstable and dependent on the situation. Indeed, I assumed infection containability when I wrote the code. The only fallback was up to 1 quarantine per turn.

easy enough to add in extra opportunities to cull, I guess

This bot seems to take longer and longer around round 36, it also takes up much more memory. I've seen it take up to 4.7 GB. Example input that takes a somewhat lengthy amount of time for this to process: http://pastebin.com/1j3K0qEv

@TheNumberOne WOW! I guess you're running this with bots that drive the infection rate up faster than 12 per round. I never anticipated it was possible for it to get that high!

MadScienceBot, Python2
You know what this world needs?
MORE SCIENCE!
How do we get MORE SCIENCE?
WITH BRAINZZ
Only cures people at the last second, couldn't care less about them except on round 50. Tries to be a zombie farm every other round
import sys, copy
import itertools

mults = {'mig_rate': -15, 'let_rate': -15, 'dead': -20, 'inf_rate': -20, 'sane': 0, 'infected': 60, 'con_rate': -30, 'id': 0}
def get_score(player_data):
    score = 0
    for k in player_data:
        score += player_data[k] * mults[k] / 100.
    return score

def add_rates(player_data):
    #Infection
    no_sane_converted = player_data["sane"]*player_data["inf_rate"]/100.
    player_data["infected"] += no_sane_converted
    player_data["sane"] -= no_sane_converted
    #Contagion
    no_sane_converted = player_data["con_rate"]
    player_data["infected"] += no_sane_converted
    player_data["sane"] -= no_sane_converted
    #Extinction
    no_killed = player_data["infected"]*player_data["let_rate"]/100.
    player_data["dead"] += no_killed
    player_data["infected"] -= no_killed

def correct(data):
    if round % 5 == 4:
        data["sane"] += int(data["sane"])/2
        data["infected"] += int(data["infected"])/2
    data["inf_rate"] += 2
    data["con_rate"] += 5
    data["let_rate"] += 5

args = sys.argv[1].split(";")
round = int(args[0])
self_id = int(args[1])
player_data = [map(int, player.split("_"))for player in args[2:]]
player_data = [dict(zip(("id", "sane", "infected", "dead", "inf_rate", "con_rate", "let_rate", "mig_rate"), player)) for player in player_data]
self_data = [player for player in player_data if player["id"] == self_id][0]

f = open("MadScienceBot.txt", "a")
f.write("\n")
f.write(`round`+"\n")
f.write("INPUT: "+`self_data`+"\n")

def m(p): p["inf_rate"] -= 4
def e(p): p["con_rate"] *= 92/100.
def i(p): p["let_rate"] -= 4
def v(p): p["inf_rate"] -= 1; p["con_rate"]-=4;p["let_rate"]-=2
def c(p): x=min(p['infected'], 10); p['infected']-=x; p['sane']+=x
def q(p): x=min(p['infected'], 30); p['infected']-=x; p['dead']+=x
def o(p): p["mig_rate"] += 10
def b(p): p["mig_rate"] -= 10

out = ""
instructions = {"M": m,
                "E": e,
                "I": i,
                "V": v,
                "C": c,
                "Q": q,
                "O": o,
                "B": b}

def run_inst(new_data, inst_id, i):
    inst = instructions[inst_id]
    if i != 2:
        inst(new_data)
        for j in new_data: new_data[j] = max(0, int(new_data[j]))
        #f.write("%s %s %s\n"%(inst_id, get_score(new_data), new_data))
    else:
        inst(new_data)
        for j in new_data: new_data[j] = max(0, int(new_data[j]))
        correct(new_data)
        add_rates(new_data)
        for j in new_data: new_data[j] = max(0, int(new_data[j]))
        #f.write("%s %s %s\n"%(inst_id, get_score(new_data), new_data))
    return new_data

def run_3_insts(self_data, insts):
    new_data = copy.copy(self_data)
    for i, inst in enumerate(insts):
        run_inst(new_data, inst, i)
    return get_score(new_data)

scores = {}
for combo in itertools.permutations(instructions.keys(), 3):
    joined = "".join(combo)
    score = run_3_insts(self_data, joined)
    scores[score] = joined
#print scores
out = scores[max(scores)]

if round == 50:
    out = "CCC"

f.write(out+"\n")
print out

The Keeper, Lua
A KotH done by a fellow french froggy ! I had to be in this contest !
This bot will do anything possible to keep its infection/contagion and lethality rates as low as possible. Its greatest priority is having a lethality near 0. It will then try to guess when it is good to try "importing" more people.
Edit: I assumed what we get via arg was sorted by playerId. It is a wrong assumption, so I added a bubble sort for datas.
input=arg[1]

datas={}
format={"playerID","sane","infected","dead","infection","contagion","lethality","migration"}
i=1
for s in input:gmatch("[^;]+")
do
  j=1
  if round==nil then round=tonumber(s)
  elseif me==nil then me=tonumber(s)+1
  else
    table.insert(datas,{})
    for r in s:gmatch("%d+")
    do
      datas[i][format[j]]=tonumber(r)
      j=j+1
    end
    i=i+1
  end
end
for i=#datas-1,1,-1
do
  for j=1,i
  do
    if datas[j].playerID>datas[j+1].playerID
    then
      datas[j],datas[j+1]=datas[j+1],datas[j]
    end
  end
end

-- First, we put ourself in a safe state
if round==1 then print("VVV")os.exit(0)end
if round==50 then print("CCC")os.exit(0)end

actions=""

-- Safety actions first
if datas[me].lethality>2
then
  actions=actions.."I"
  datas[me].lethality=datas[me].lethality-4>0 and datas[me].lethality-4 or 0
end

if datas[me].infected>=10
then
  if(datas[me].infection+datas[me].contagion+datas[me].lethality>4)
  then
    actions=actions.."V"
    datas[me].infection=datas[me].infection-1>0 and datas[me].infection-1 or 0
    datas[me].contagion=datas[me].contagion-4>0 and datas[me].contagion-4 or 0
    datas[me].lethality=datas[me].lethality-2>0 and datas[me].lethality-2 or 0
  end
  actions=actions.."C"
  datas[me].sane=datas[me].sane+10
  datas[me].infected=datas[me].infected-10
end

-- We can now try taking some initiatives
while #actions<3
do
  rates={}
  for i=1,#datas
  do
    if i~=me
    then
      table.insert(rates,(datas[i].infected/datas[i].sane>0 and datas[i].sane or 0)*(datas[i].migration/100))
    end
  end
  rates["total"]=0
  for i=1,#rates
  do
    rates.total=rates.total+rates[i]
  end
  rates.total=(rates.total/#rates)*100

  if datas[me].migration<=15 and datas[me].migration+10>rates.total
  then
    actions=actions.."O"
    datas[me].migration=datas[me].migration+10>0 and datas[me].migration+10 or 0
  elseif (datas[me].sane/datas[me].infected)*100<rates.total
  then
    actions=actions.."B"
    datas[me].migration=datas[me].migration-10>0 and datas[me].migration-10 or 0
  elseif datas[me].infected>=10
  then
    actions=actions.."C"
    datas[me].infected=datas[me].infected-10
  else
    actions=actions.."V"
    datas[me].infection=datas[me].infection-1>0 and datas[me].infection-1 or 0
    datas[me].contagion=datas[me].contagion-4>0 and datas[me].contagion-4 or 0
    datas[me].lethality=datas[me].lethality-2>0 and datas[me].lethality-2 or 0
  end
end
print(actions)
os.exit(0)

@Thrax Oh.. Corrected the source code, now use input=arg[1] instead of input=io.read().

ZombieState, Java
Hey, this is my first post on this site. I basically just took one of the example bots and changed the lines regarding the output.

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

public class ZombieState {

int round;
int phase;
int playerID;
int thisTownID;

List<State> states;
List<State> otherStates;

State thisState;

public static void main(String[] args){
    new ZombieState().sleep(args[0].split(";"));
}

private void sleep(String[] args) {

    round = Integer.parseInt(args[0]);
    thisTownID = Integer.parseInt(args[1]);

    states = new ArrayList<>();
    otherStates = new ArrayList<>();

    for (int i = 2; i < args.length; i++){
        states.add(new State(args[i]));
    }

    for (State state : states){
        if (state.isMine()) {
            thisState = state;
        } else {
            otherStates.add(state);
        }
    }

    StringBuilder sb = new StringBuilder();
    if(round == 1)
        System.out.println("TTT");
    else if(round == 50)
        System.out.println("CCC");
    else
    {
        while(thisState.lethalityRate >= 4)
        {
            sb.append("I");
            thisState.lethalityRate -= 4;
        }
        sb.append("DDD");
        System.out.println(sb.toString().substring(0, 3));
    }
}

private class State {

    private final int ownerId;
    public int sane;
    public int infected;
    public int dead;
    public int infectionRate;
    public int contagionRate;
    public int lethalityRate;
    public int migrationRate;

    public State(String string) {
        String[] args = string.split("_");
        ownerId = Integer.parseInt(args[0]);
        sane = Integer.parseInt(args[1]);
        infected = Integer.parseInt(args[2]);
        dead = Integer.parseInt(args[3]);
        infectionRate = Integer.parseInt(args[4]);
        contagionRate = Integer.parseInt(args[5]);
        lethalityRate = Integer.parseInt(args[6]);
        migrationRate = Integer.parseInt(args[7]);
    }

    public int getOwnerId() {
        return ownerId;
    }

    public boolean isMine(){
        return getOwnerId() == playerID;
    }

}

}

I hope this is ok and the bot did quite well in my own runs. Because who needs the living if you can have 30 healthy and a maximum amount of infected at the end. It starts the game with 3x BioTerrorism to get everything started and trys to keep the local lethality low. If it is less than 4 it trys to up the global infection and contagion rate with Dissemination.

Welcome to PPCG :-) Hope you have a good time here

This seems to be doing good so far. Welcome to PPCG, and good job!

Strategist, Python
This bot is really serious about surviving. He's analysed the possible strategies, and come up with his own winning method. Which he's now going to document in source comments, because he's a nice guy and wants other people to survive, too.
Called with python strategist.py.
import sys
import random
import math

def main():
    id = int(get_player_id(sys.argv[1]))
    stats = get_player_stats(sys.argv[1], id)
    round = int(get_round(sys.argv[1]))

    if id == -1 or stats == None or round == -1:
        # Something is wrong here. RED ALERT! Close all possible entry routes and set
        # quarantine levels to maximum!
        print("BQQ")
        sys.exit(1)

    if round == 1:
        # remove migration, cure some infected, and remove some danger
        print("BCM")
    elif round % 5 == 4:
        # Rounds 4, 9, 14 etc. One before repopulation. We want as many Healthy and as
        # few Infected as possible to reproduce. Prioritise curing infected, because that
        # maximises Healthy for reproduction. If that's not possible, quarantine them.
        quarantine = math.ceil(int(stats['infected']) / 30)
        cure = math.ceil(int(stats['infected']) / 10)
        if cure <= 3:
            # we can deal with all our infections within 3 cures
            output = "C" * cure
            for i in range(3 - cure):
                # got moves left? Great, remove some danger.
                output += get_random_science()
            print(output)
        elif quarantine <= 3:
            # we can deal with all our infections within 3 quarantines
            output = "Q" * quarantine
            for i in range(3 - quarantine):
                # got moves left? Great, remove some danger.
                output += get_random_science()
            print(output)
        else:
            # We can't deal with all the infected in one round, so deal with some. Yes, we
            # don't get rid of as many as we could here, but we're about to reproduce so
            # we want Healthies in the next round.
            print("QQC")
    else:
        output = ""
        if int(stats['infected']) <= 10:
            # we can deal with all our infections by curing them
            output += "C"
        elif int(stats['infected']) <= 30:
            # we can deal with all our infections by quarantining them
            output += "Q"
        elif int(stats['infected']) >= int(stats['healthy']) * 0.5:
            # we're getting overrun with infected people, get rid of some
            output = "QCC"

        for i in range(3 - len(output)):
            # if we haven't used all our moves, we can remove some danger factors
            output += get_random_science()

        print(output)

def get_random_science():
    return random.choice(["M", "E", "I", "V"])

def get_player_id(args):
    splat = args.split(";")
    return splat[1] if len(splat) >= 2 else -1

def get_player_stats(args, id):
    splat = args.split(";")
    players_data = [x for x in splat if "_" in x]
    my_data = [y for y in players_data if y.split("_")[0] == str(id)]
    data_splat = my_data[0].split("_")

    if len(data_splat) == 8:
        # Id, Healthy, Infected, Dead, InfRate, ConfRate, LethRate, MigRate
        return {
            'healthy': data_splat[1],
            'infected': data_splat[2],
            'dead': data_splat[3],
            'inf_rate': data_splat[4],
            'conf_rate': data_splat[5],
            'leth_rate': data_splat[6],
            'mig_rate': data_splat[7]
        }
    else:
        return None

def get_round(args):
    splat = args.split(";")
    return splat[0] if len(splat) >= 1 else -1

if __name__ == "__main__":
    main()

OpenAndClose
Start the game by opening the borders, then let all the sick come. After we have a large sick population (round 30), close the borders and work on curing the sick.
#You can copy this code if you want. Not specific to my strategy.
PlayerId = 0
Healthy = 1
Infected = 2
Dead = 3
InfectionRate = 4
ContagionRate = 5
LethalityRate = 6
MigrationRate = 7

a = ARGV[0].split ';'
round = a.shift.to_i
my_id = a.shift.to_i
players = a.map{|s|s.split('_').map{|str| str.to_i}}

my_index = players.index{|p|
    p[PlayerId] == my_id
}
me = players[my_index]

#strategy specific code starts here.

commands = ""
if round < 30
  commands += me[MigrationRate] == 100 ? (me[InfectionRate] <= 1 ? "V" : "M") : "O"
  commands += me[LethalityRate] <= 2 ? "V" : "I"
  commands += me[ContagionRate] <= 4 ? "V" : "E"
elsif round < 50
  commands += me[MigrationRate] == 0 ? "V" : "B"
  commands += me[LethalityRate] < 20 ? "C" : "I"
  commands += me[ContagionRate] <  5 ? "C" : "E"
else
  commands = "CCC"
end

print commands
$stdout.flush

DisseminationBot, Ruby
This bot will Cure as long as 10 or more are left to cure. Next, if the infection rate is at least 4, the bot will decrease it. All other actions are spent increasing the contagion rate, which will not hurt me, because I have no infected left.
#You can copy this code if you want. Not specific to my strategy.
PlayerId = 0
Sane = 1
Infected = 2
Dead = 3
InfectionRate = 4
ContagionRate = 5
LethalityRate = 6
MigrationRate = 7

a = ARGV[0].split ';'
round = a.shift.to_i
my_id = a.shift.to_i
players = a.map{|s|s.split('_').map{|str| str.to_i}}

my_index = players.index{|p|
    p[PlayerId] == my_id
}
me = players[my_index]

#strategy specific code starts here.

commands = ""
commands += 'C' if me[Infected] >= 10
commands += 'C' if me[Infected] >= 20
commands += 'C' if me[Infected] >= 30
commands += 'M' if me[InfectionRate] >= 4 and commands.length < 3
commands += 'D' while commands.length < 3

print commands
$stdout.flush

XenoBot (Node.js)
XenoBot is afraid of people, his solution to the epidemic is to isolate his population, cure the people who he can, and isolate them when he can't. He doesn't bother with all this warfare nonsense, he's just trying to keep his people alive.
Activate XenoBot like so:
node xenobot.js [data]

Code:
const argv = String(process.argv),
    data = argv.split(";"),
    round = data[0],
    id = Number(data[1]),
    info = data[id + 1].split("_"),
    sane = info[1],
    infected = info[2],
    dead = info[3],
    infectionRate = info[4],
    contagionRate = info[5],
    lethalityRate = info[6],
    migrationRate = info[7]

var moves = 3
function exec(a) {
  process.stdout.write(a)
}
if(migrationRate >= 10) {
  exec("B")
}
if (infectionRate >= 8) {
  exec("MQ")
  moves-=2;
} else if(contagionRate >= 16) {
  exec("EC")
  moves-=2;
} else if(lethalityRate >= 8) {
  exec("IV")
  moves--;
} else {
  exec("B");
  moves--;
}

if (sane / 3 > infected + dead) {
  exec("Q")
  moves--;
}
if(moves > 0) {
  exec("B")
}

Researcher, Java
This bot focuses on research. If the number of infected is below 15, it tries to cure them. If it's higher than that, it chooses the more effective solution.

public class Researcher {
    public static void main(String[] args){
        String[] args1 = args[0].split(";");
        int id = Integer.parseInt(args1[1]);
        for (int i = 2; i < args1.length; ++ i) {
            String[] args2 = args1[i].split("_");
            if (Integer.parseInt(args2[0]) == id) {
                int infected = Integer.parseInt(args2[2]);
                if (infected == 0) {
                    System.out.println("MEI");
                } else if(infected < 15) {
                    System.out.println("MEC");
                } else {
                    System.out.println("MEQ");
                }
            }
        }
    }
}

Java Language, I presume??

Two more Python bots
Israel
It's similar to Mooch, but maybe not quite as good as Mooch, except in rare occasions when it's much better:
import sys
a = sys.argv[1].split(';')
round = int(a[0])
myid = a[1]
players = {}
Sane = 0
Infected = 1
Dead = 2
InfectionRate = 3
ContagionRate = 4
LethalityRate = 5
MigrationRate = 6
for i in range(2,len(a)):
    b = a[i].split('_')
    players[b[0]]=map(int,b[1:])
output=''
if round<=4:output = ["OOO","OOO","OOO","OII"][round-1]
if round==50: output = "CCC"
mycontrate = players[myid][ContagionRate]
myfatrate = players[myid][LethalityRate]
myinfrate = players[myid][InfectionRate]
if myinfrate+mycontrate<5:
    output+="V"
    myfatrate-=2
    if round < 47:
        output+="I"*(myfatrate/4)
        if myfatrate%4: output+="V"
else:
    if round < 47:
        output+="I"*(myfatrate/4)
        if myfatrate%4: output+="V"
    output+="M"*(myinfrate/4)
    if round < 47:
        output+="E"*(mycontrate/4)
output+="CCC"

print output[:3]

Red Cross
Sort of like pacifist, except tries also to keep its own people from dying. Fails miserably at this, but it's nice to have another friendly on the playing field.
import sys
a = sys.argv[1].split(';')
round = int(a[0])
myid = a[1]
players = {}
Sane = 0
Infected = 1
Dead = 2
InfectionRate = 3
ContagionRate = 4
LethalityRate = 5
MigrationRate = 6
for i in range(2,len(a)):
    b = a[i].split('_')
    players[b[0]]=map(int,b[1:])
output="PPPPP"
if round<=4:output = ["OOO","OOO","OOO","OII"][round-1]
elif round==50: output = "CCC"
else: output = output[:2-3*round%5]+"I"+output[2-3*round%5:]
print output[:3]

Smaug (Python)
I am fire; I am death.
Smaug creates as much death as possible, regardless of where it occurs.
# "I am fire, I am death"
# Smaug has two goals: hoard gold and bring death...
#    and this world seems to be all out of gold

from sys import argv
args = argv[1].split(";")

round = int(args.pop(0))
me = int(args.pop(0))

if round==50: # can't cause more death on the last round, might as well infect
    print('TTT')

def predict_infected(infected, infection_rate, contagion_rate):
    i = infected + infection_rate
    return i + int(i*contagion_rate)

def predict_dead(infected, lethality_rate):
    return int(infected*lethality_rate)

strategies = {'WWW':0, 'WWD':0, 'WDD':0, 'DDD':0}
for player in args:
    player=player.split('_')
    healthy=int(player[1])
    infected=int(player[2])
    infection_rate=int(player[4])
    contagion_rate=int(player[5])/100.
    lethality_rate=int(player[6])/100.

    if round%5==0:
        healthy+=healthy/2
        infected+=infected/2

    pi_old = predict_infected(infected, infection_rate, contagion_rate)
    pd_old = predict_dead(pi_old, lethality_rate)

    for strat in strategies:
        ir_new = infection_rate + 3
        lr_new = lethality_rate + (strat.count('W')*.02)
        cr_new = contagion_rate + (strat.count('D')*.02)

        pi_new = predict_infected(infected, ir_new, cr_new)
        pd_new = predict_dead(pi_new, lr_new)

        increase = pd_new - pd_old

        strategies[strat]+=increase

print max(strategies, key=strategies.get)

If you're trying to golf a polyglot, the empty file would be shorter and do exactly what the watcher does (and also, as it happens, exactly what PassiveBot does--the two behave identically), because the interpreter substitutes NNN for an 0 length response.

@quintopia I wasn't trying to, just realized when I typed it that it conveniently worked as a polyglot, but I didn't realize what PassiveBot did, so I'm just gonna delete Watcher (no point in having two identical ones)

Remove Infected (Python)
Despite all the random logic, I'm pretty it's rare for this to return anything but Q's and C's (preventative measures never seem that helpful). Oh well. Might borrow some of it for another bot, but leaving it in in case it helps.
# Remove as many of it's own infected as possible, preferably by curing, but quarantining if it's getting out of hand
# If not very many can be cured, takes preventative measures (B,E,M, or V)

from sys import argv

CMDS=3
C_RATE=10
E_RATE=.08
M_RATE=4
Q_RATE=30
V_RATE=(1,.04)

def find_me(args):
    for player in args:
        player=player.split('_')
        if int(player[0])==me:
            return player

def actions_available():
    global actions
    if len(actions) < CMDS:
        return True
    else:
        return False

def add_actions(new_actions):
    global actions
    actions = (actions + new_actions)[0:CMDS]

def get_remaining_infected(local_infected):
    global actions
    return local_infected - (Q_RATE*actions.count('Q')) - (C_RATE*actions.count('C'))

def too_many_infected(local_infected):
    max_infected = C_RATE*(CMDS+1) # If we can get down to 10 or less without quarantining, that's good
    if local_infected > max_infected:
        return True
    else: return False

def projection(infection_rate, remaining_infected, action):
    additional_M=0
    additional_E=0
    additional_V=0

    if action == "M":
        additional_M=1
    elif action == "E":
        additional_E=1
    else:
        additional_V=1

    M_level = M_RATE*(actions.count('M')+additional_M)
    E_level = E_RATE*(actions.count('E')+additional_E)
    V_level = (V_RATE[0]*(actions.count('V')+additional_V), V_RATE[1]*(actions.count('V')+additional_V))

    projection = infection_rate - M_level - V_level[0] + (remaining_infected * (contagion_rate - E_level - V_level[1]))
    return int(projection)

def get_best_action(local_infected):
    global actions
    remaining_infected = get_remaining_infected(local_infected)

    # If we can leave no infected, do so
    if remaining_infected <= C_RATE and remaining_infected >= 0:
        return 'C'

    strategies = {'M':0, 'E':0, 'V':0,'C':min(remaining_infected,C_RATE)}

    pni = int(infection_rate + (remaining_infected*contagion_rate)) # predicted new infected
    strategies['M'] = pni - projection(infection_rate, remaining_infected, 'M')
    strategies['E'] = pni - projection(infection_rate, remaining_infected, 'E')
    strategies['V'] = pni - projection(infection_rate, remaining_infected, 'V')
    # any benefit to including P as an option?

    #print(strategies)
    max_saved = 'C'
    for strat,saved in strategies.iteritems():
        if saved > strategies[max_saved]:
            max_saved=strat
        elif saved == strategies[max_saved]:
            #prioritize V because of it's extra benefit of reducind lethality_rate
            max_saved=max(strat,max_saved)

    if strategies[max_saved] <= C_RATE/2:
        # can't save that many, just close borders instead
        selected_action = 'B'
    else: selected_action = max_saved
    return selected_action

args = argv[1].split(";")
round = int(args.pop(0))
me = int(args.pop(0))
actions = ""

my_town = find_me(args)

local_infected = int(my_town[2])
infection_rate = int(my_town[4])
contagion_rate = int(my_town[5])/100.

if round!=50 and too_many_infected(local_infected):
    # Things are getting out of hand, quarantine and consider preventative measures
    actions = ('Q'*(local_infected/Q_RATE))[0:CMDS]

    while actions_available():
        add_actions(get_best_action(local_infected))
else: actions='CCC'

print ''.join(sorted(actions)) # always cure first

@Thrax This bot's been updated

CureThenQuarantine, Java
The state has instigated a policy of curing those lucky few and then quarantining the remainder of infected persons. Once the infected population is reduced then the focus on reducing local rates and then help reducing the global rates.
The borders are shut to ensure no infected migration into the state.
I have only tested the bot against java and python bots... it seems to hold its own against them. It also seems that my bot behaves similar to CullBot.
public class CureThenQuarantine {
    static int playerID;

    public static void main(String[] args)
    {
        State thisState=null;

        args = args[0].split(";");

        // Parse arguments
        int round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        for (int i = 2; i < args.length; i++){
            thisState = new State(args[i]);
            if(thisState.isMine()){
                break;
            }
        }

        String action="";
        if(round == 1) action = "B"; // ensure no migration.
        else if (round == 50 ) action ="CCC"; // not much else we can do so just cure some people.

        // Highest Priority to Curing and then Quarantining infected, but do not perform either action if it would be wasteful.
        if (thisState.infected>9)
        {
            if (thisState.infected<19) action+="C";
            else if (thisState.infected<29) action+="CC";
            else if (thisState.infected<39) action+="CCC";
            else if (thisState.infected<49) action+="CQ";
            else if (thisState.infected<59) action+="CCQ";
            else if (thisState.infected<79) action+="CQQ";
            else action+="QQQ";
        }

        // Next priority is to reduce infection rate
        if (thisState.infectionRate>8) action+="MMM";
        else if (thisState.infectionRate>4) action+="MM";
        else if (thisState.infectionRate>1) action+="M";
        else if (thisState.infectionRate>0) action+="V";

        // then reduce contagion rate
        if (thisState.contagionRate>16) action+="EEE";
        else if (thisState.contagionRate>8) action+="EE";
        else if (thisState.contagionRate>1) action+="E";
        else if (thisState.contagionRate>0) action+="V";

        // and least priority is lethality rate... since we are only going to quarantine infected persons anyway.
        if (thisState.lethalityRate>8) action+="III";
        else if (thisState.lethalityRate>4) action+="II";
        else if (thisState.lethalityRate>1) action+="I";
        else if (thisState.lethalityRate>0) action+="V";

        // and if we have managed to clean up our state then we help others states.
        action+="PPP";

        System.out.println(action.substring(0,3));
    }

    static private class State {
        public int ownerId;
        public int lethalityRate;
        public int healthy;
        public int infected;
        public int infectionRate;
        public int contagionRate;

        public State(String string) {
            String[] args = string.split("_");
            ownerId = Integer.parseInt(args[0]);
            healthy = Integer.parseInt(args[1]);
            infected = Integer.parseInt(args[2]);
            infectionRate = Integer.parseInt(args[4]);
            contagionRate = Integer.parseInt(args[5]);
            lethalityRate = Integer.parseInt(args[6]);
        }

        public boolean isMine(){
            return ownerId == playerID;
        }
        public String toString()
        {
            return "H: "+healthy+" I: "+infected+" IR: "+infectionRate+" LR: "+lethalityRate+" CR: "+contagionRate;
        }
    }
}

Piecemeal, Java
Based on my previous bot (CureThenQuarantine), I have found that with the aggressive bots in play, there is no need for quarantine as the infected will die very quickly, so this bot will opportunistically cure 10 infected each turn (either arriving from migration or from infections from healthy population). It will then use the remaining actions to ensure the healthy population stays healthy relying on births to boost the healthy population.
The borders are shut to ensure no infected migration into the state.
public class Piecemeal{
    static int playerID;

    public static void main(String[] args)
    {
        State thisState=null;

        args = args[0].split(";");

        // Parse arguments
        int round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        for (int i = 2; i < args.length; i++){
            thisState = new State(args[i]);
            if(thisState.isMine()){
                break;
            }
        }

        String action="";
        if(round == 1) action = "B"; // ensure no migration.
        else if (round == 50 ) action ="CCC"; // not much else we can do so just cure some people.

        // Highest Priority to Curing up to ten infected if there are any.
        if (thisState.infected>0)
        {
            action+="C";
        }

        // Next priority is to reduce infection rate
        if (thisState.infectionRate>8) action+="MMM";
        else if (thisState.infectionRate>4) action+="MM";
        else if (thisState.infectionRate>1) action+="M";
        else if (thisState.infectionRate>0) action+="V";

        // then reduce contagion rate
        if (thisState.contagionRate>16) action+="EEE";
        else if (thisState.contagionRate>8) action+="EE";
        else if (thisState.contagionRate>1) action+="E";
        else if (thisState.contagionRate>0) action+="V";

        // and least priority is lethality rate... since we are only going to quarantine infected persons anyway.
        if (thisState.lethalityRate>8) action+="III";
        else if (thisState.lethalityRate>4) action+="II";
        else if (thisState.lethalityRate>1) action+="I";
        else if (thisState.lethalityRate>0) action+="V";

        // and if we have managed to clean up our state then we help others states.
        action+="PPP";

        System.out.println(action.substring(0,3));
    }

    static private class State {
        public int ownerId;
        public int lethalityRate;
        public int healthy;
        public int infected;
        public int infectionRate;
        public int contagionRate;

        public State(String string) {
            String[] args = string.split("_");
            ownerId = Integer.parseInt(args[0]);
            healthy = Integer.parseInt(args[1]);
            infected = Integer.parseInt(args[2]);
            infectionRate = Integer.parseInt(args[4]);
            contagionRate = Integer.parseInt(args[5]);
            lethalityRate = Integer.parseInt(args[6]);
        }

        public boolean isMine(){
            return ownerId == playerID;
        }
        public String toString()
        {
            return "H: "+healthy+" I: "+infected+" IR: "+infectionRate+" LR: "+lethalityRate+" CR: "+contagionRate;
        }
    }
}

BlunderBot, Java
BlunderBot does its best to keep people healthy by reducing infection rate and curing.  Thereafter, reduce contagion rate. If there are still turns, reduce lethality, then be a pacifist.
import java.util.ArrayList;
import java.util.List;

public class BlunderBot {

int round;
int phase;
int playerID;
int thisTownID;

List<State> states;
List<State> otherStates;

State thisState;
String action = "";
int cc=0; // command count

public static void main(String[] args){
    new BlunderBot().sleep(args[0].split(";"));
}

private void action(String newAction) {
    action += newAction;
    cc+= newAction.length();
    if (cc>=3) {
        System.out.println(action.substring(0, 3));
        System.exit(0);;
    }
}
private void sleep(String[] args) {

    round = Integer.parseInt(args[0]);
    thisTownID = Integer.parseInt(args[1]);

    states = new ArrayList<>();
    otherStates = new ArrayList<>();

    for (int i = 2; i < args.length; i++){
        states.add(new State(args[i]));
    }

    for (State state : states){
        if (state.isMine()) {
            thisState = state;
        } else {
            otherStates.add(state);
        }
    }

    // Round specific commands
    if (round == 1)                                 { action("B");   }
    if (round == 50)                                { action("CCC"); }

    for (int i=0;i<3;i++){
      if (thisState.getInfectionRate() >= 4)    { action("M"); thisState.infectionRate -= 4;}
    }

    if (thisState.getInfected() >= 5)   {
        if (thisState.getInfected() < 15)           { action("C");   }
        else if (thisState.getInfected() < 25)      { action("CC");  }
        else if (thisState.getInfected() < 35)      { action("CCC"); }
        else if (thisState.getInfected() < 45)      { action("QC");  }
        else if (thisState.getInfected() < 55)      { action("QCC"); }
        else if (thisState.getInfected() < 65)      { action("QQ");  }
        else if (thisState.getInfected() < 75)      { action("QQC"); }
        else if (thisState.getInfected() < 85)      { action("QQC"); }
        else                                        { action("QQQ"); }
    }

    for (int i=0;i<3;i++){
        if (thisState.getContagionRate() >= 8)      { action("E"); thisState.contagionRate -= 8;}
    }

    for (int i=0;i<3;i++){
        if (thisState.getLethalityRate() >= 4)      { action("I"); thisState.lethalityRate -= 4;}
    }

    // Nothing else to do
    action("PPP");

}

private class State {

    private final int ownerId;
    private int healthy;
    private int infected;
    private int dead;
    private int infectionRate;
    private int contagionRate;
    private int lethalityRate;
    private int migrationRate;

    public State(String string) {
        String[] args = string.split("_");
        ownerId = Integer.parseInt(args[0]);
        healthy = Integer.parseInt(args[1]);
        infected = Integer.parseInt(args[2]);
        dead = Integer.parseInt(args[3]);
        infectionRate = Integer.parseInt(args[4]);
        contagionRate = Integer.parseInt(args[5]);
        lethalityRate = Integer.parseInt(args[6]);
        migrationRate = Integer.parseInt(args[7]);
    }

    public int getOwnerId() {
        return ownerId;
    }

    public int getHealthy() {
        return healthy;
    }

    public int getInfected() {
        return infected;
    }

    public int getDead() {
        return dead;
    }

    public int getInfectionRate() {
        return infectionRate;
    }

    public int getContagionRate() {
        return contagionRate;
    }

    public int getLethalityRate() {
        return lethalityRate;
    }

    public int getMigrationRate() {
        return migrationRate;
    }

    public boolean isMine(){
        return getOwnerId() == thisTownID;
    }

}

}

Local results after 10 games (50 rounds) with Java bots:
################################
1. BlunderBot (679, 0, 267)
2. CureThenQuarantine (407, 0, 277)
3. Graymalkin (302, 0, 217)
4. InfectedTown (30, 2298, 436)
5. Triage (30, 1095, 579)
6. ZombieState (30, 1043, 590)
7. EvilBot (30, 549, 540)
8. Mooch (30, 434, 327)
9. Madagascar (1, 0, 152)
10. BipolarBot (1, 0, 223)
11. InfectionBot (0, 1049, 542)
12. MedicBot (0, 1, 156)
13. Researcher (0, 0, 72)
14. TheCure (0, 0, 89)
15. PassiveBot (0, 0, 134)
16. WeaponOfMassDissemination (0, 0, 135)
17. BioterroristBot (0, 0, 135)
18. TrumpBot (0, 0, 234)

UndecidedBot, Python
This bot can't decide what the best strategy is, so he's trying to mix three of them.

Quarantine. Seems to be pretty effective at removing infected people, who we don't want around.
Science. One of the sciences (M, E, I, V, or C) benefits us by reducing some of our danger rates.
Random. May the best pRNG win.

Called by using python undecided.py
import sys
import random

def main():
    print("{0}{1}{2}".format("Q", get_random_science(), get_random_action()))

def get_random_action():
    return random.choice(["M", "E", "I", "V", "C", "Q", "O", "B", "T", "W", "D", "P"])

def get_random_science():
    return random.choice(["M", "E", "I", "V", "C"])

if __name__ == "__main__":
    main()

I get "TypeError: choice() takes 2 positional arguments but 6 were given" on line 11.

@Mwr247that would be because I wrote it off the top of my head. Try that.

No errors, but still prints nothing. Change your return in main to a print and I think you're good. EDIT: Weird, I'm still getting invalid/blank responses sometimes.

@Mwr247 helps if I output to STDOUT, I guess. There, I've tested it now.

Yup, that works! :D

Socialist, Python 3
A glorious regime, which thinks all other countries are evil, so they will try to kill them. They do care for their people, so if they get infected they will take action.
Edit:Fixed the type comparison
 from sys import argv
 data=argv[1].split(";")[2].split("_")
 inf=int(data[2])
 hel=int(data[1])
 dead=int(data[3])
 if inf>29:
     print("CQM") # if there is to many sick cure some, quarantine some, and try to get rid of the disease
 else:
     if hel<80:
     print("BMV") # if there's few healthy, close our borders, because it's other countries
     else:
         if dead>19:
         print("WMD") # if we have too many dead attack others, but cure ourselves
         else:
         print("TWD") # if none of these are true we want to kill everyone else

I get an error on line 6 stating that you can't compare integers to strings.

Forgot about the type, I will fix that( I got too use to JavaScript an its weird behavior)

Fixed, sorry it took so long, I didn't have the time.

InfectionBot
Splitted off into a seperate answer

int lethal=10;
if(round == 1) System.out.print("OOO");
else if (round == 50) System.out.print("CCC");
else{
  {
    for(int x=0;x<3;x++) {
      if(thisState.getLethalityRate() <=6 && x == 0) {
        if(lethal<50) {
          System.out.print("W");
          lethal = lethal + 3;
          }
        else System.out.print("D");
      } else if(thisState.getLethalityRate()<=2 && x == 1) {
        if(lethal<50) {
          System.out.print("W");
          lethal = lethal + 3;
          }
        else System.out.print("D");
      } else if(thisState.getLethalityRate() ==0 && x == 2) {
        System.out.print("D");
      } else System.out.print("I");
    }
}
}

This Bot is basically an ass trying too flood everyone with his infected by having them migrate and inreasing infection lethality and contagation while lowering his own lethality to 0.
Edit: swapped D and I at the end so it gets more Lethality reduce

Obamacare, Python 3
A little late and a little short, but it should work.
import sys

#get input
stdin = sys.argv[1].split(";")

#unpack input
rnd = stdin[0]
stdin.pop(0)
bot = stdin[0]
stdin.pop(0)
#find self
town = []
for player in stdin:
    if player.split("_")[0] == str(bot):
        town = player.split("_")
        break
#unpack town
healthy = town[1]
infected = town[2]

out = []

#get down to business
if int(infected) < int(healthy):
    out.append("ME")
if int(infected) >= int(healthy):
    out.append("CI")
if int(infected) >= 30:
    out.append("C")
else:
    out.append("T")

sys.stdout.write("".join(out))

BipolarBot, Java
BipolarBot starts off playing nice, building up a bit of a population (and letting others do the same).
It then flips and inflicts as much harm on others as possible, while trying to keep its local population from dying out completely. Changed the scoreboard quite a bit when I included it in my local games.
import java.util.ArrayList;
import java.util.List;

public class BipolarBot {

int round;
int phase;
int playerID;
int thisTownID;

List<State> states;
List<State> otherStates;

State thisState;
String action = "";
int cc=0; // command count

public static void main(String[] args){
    new BipolarBot().sleep(args[0].split(";"));
}

private void action(String newAction) {
    action += newAction;
    cc+= newAction.length();
    if (cc>=3) {
        System.out.println(action.substring(0, 3));
        System.exit(0);;
    }
}
private void sleep(String[] args) {

    round = Integer.parseInt(args[0]);
    thisTownID = Integer.parseInt(args[1]);

    states = new ArrayList<>();
    otherStates = new ArrayList<>();

    for (int i = 2; i < args.length; i++){
        states.add(new State(args[i]));
    }

    for (State state : states){
        if (state.isMine()) {
            thisState = state;
        } else {
            otherStates.add(state);
        }
    }

    // Round specific commands
    if (round == 1)                                     { action("B");   }
    if (round == 50)                                    { action("CCC"); }

    if ((round) <= 30)  {
        for (int i=0;i<3;i++){
            if (thisState.getInfectionRate() >= 4)      { action("M"); thisState.infectionRate -= 4;  }
        }

        if (thisState.getInfected() >= 5)   {
            if (thisState.getInfected() < 15)           { action("C");   }
            else if (thisState.getInfected() < 25)      { action("CC");  }
            else                                        { action("CCC"); }
        }

        for (int i=0;i<3;i++){
            if (thisState.getContagionRate() >= 8)      { action("E");  thisState.contagionRate -= 8; }
        }

        for (int i=0;i<3;i++){
            if (thisState.getLethalityRate() >= 4)      { action("I"); thisState.lethalityRate -= 4;  }
        }

        // Nothing else to do, be nice
        action("PPP");
    }

    else {
        for (int i=0;i<3;i++){
            if (thisState.getLethalityRate() >= 4)      { action("I"); thisState.lethalityRate -= 4;  }
        }

        // Nothing else to do, cause trouble.
        action("TTT");

    }

}

private class State {

    private final int ownerId;
    private int healthy;
    private int infected;
    private int dead;
    private int infectionRate;
    private int contagionRate;
    private int lethalityRate;
    private int migrationRate;

    public State(String string) {
        String[] args = string.split("_");
        ownerId = Integer.parseInt(args[0]);
        healthy = Integer.parseInt(args[1]);
        infected = Integer.parseInt(args[2]);
        dead = Integer.parseInt(args[3]);
        infectionRate = Integer.parseInt(args[4]);
        contagionRate = Integer.parseInt(args[5]);
        lethalityRate = Integer.parseInt(args[6]);
        migrationRate = Integer.parseInt(args[7]);
    }

    public int getOwnerId() {
        return ownerId;
    }

    public int getHealthy() {
        return healthy;
    }

    public int getInfected() {
        return infected;
    }

    public int getDead() {
        return dead;
    }

    public int getInfectionRate() {
        return infectionRate;
    }

    public int getContagionRate() {
        return contagionRate;
    }

    public int getLethalityRate() {
        return lethalityRate;
    }

    public int getMigrationRate() {
        return migrationRate;
    }

    public boolean isMine(){
        return getOwnerId() == thisTownID;
    }

}
}

BackStabber
Acts rather friendly till the end curing its people and being passive, then it goes insane converting global healthy to infected.
from sys import argv
round=int(argv[1].split(";")[0])
if round==1:
    print("VPO")
elif round<11:
    print("VCO")
elif round<23:
    print("VCC")
elif round<35:
    print("VCB")
elif round<41:
    print("VPB")
elif round<46:
    print("CTT")
else:
    print("TTT")