Treasure Hunting on a Deserted Island

13

4

Introduction

You are stranded on a deserted island with some servants and are hunting for treasure. The longer one searches, the more treasure one finds. The fewer people searching, the more each person finds.

Due to limited supplies, the leader has decided that a few people, up to a quarter of the group, shall be left to die each night. He has decided not to tell anyone exactly how many people shall die on any given day ahead of time.

You are in control of a small group of 5 people, who shall venture out of camp to find treasure for you.

Objective

The objective of this competition is to amass as much treasure as possible. Every turn that your servants do not attempt to return to camp, they will find a certain number of pieces of treasure. Your servants may return to camp at different times.

Each turn that a worker stays out to search for treasure, the worker finds 1+R pieces of treasure, where R is the number of workers (out of all the bots) already back in camp. Dead bots do not factor into this calculation.

At the start of each day, a random number (n) from 2 to max(3, floor(num_live_players/4)) will be chosen. (For 10 players on day 1, this is 2 to max(3,50/4)=12. For 20 players on day 1, this would be 2 to max(3,100/4)=25.) This number represents the number of players who will be left to die for that day, and will not be given to your program.

If a servant is one of the last n people to return, he/she will die and be unable to transfer the treasure he/she found to your possession. Furthermore, the servant will be unable to participate in treasure hunting for the rest of the adventure.

Your final score is the average amount of treasure you obtained per adventure (run of the controller).

If more people attempt to return to camp on the same turn than there are open slots, random numbers will determine who gets in and who dies.

A day on this island from sunrise to sunset lasts 30 turns. As there are many dangerous animals at night, failure to return by sunset means that you will not be allowed into the camp.

Input/Output

Your program should run for the entirety of the simulation.

At the start of the simulation, INDEX I will be inputted, where I is the index of your bot (this index is counted from 1 up).

At the start of each day, START_DAY D/N will be inputted to your program, where D is the day number (starting from 1), and N is equal to max(3, floor(num_live_players/4)), which is the maximum number of people who may die on that particular day.

At the start of each turn, START_TURN T will be inputted to your program, where T is the turn number (starting from 1).

Once your program receives this, it should respond with a list of your servants' moves, each separated by a comma.

Valid moves are:

  • R: Try to return to camp.
  • S: Stay looking for treasure.
  • N: Servant is already dead or in camp.

Entering an invalid move will be interpretted as S if the bot is alive and not in camp, and N otherwise.

At the end of each turn, a string shall be passed to your program:

END_TURN [Turn #] [Bot 1 Moves] [Bot 2 Moves] ...

where each bot's servants' moves are separated by commas.

These moves will be one of the following:

  • R: Successfully returned to camp that turn.
  • r: Failed to return to camp that turn.
  • S: Still looking for treasure.
  • D: Died on an earlier turn.
  • N: Already back at camp.

Bots and servants remain in the same order throughout the entire simulation.

For example:

INDEX 2
....
END_TURN 8 N,N,N,N,N r,r,r,r,D D,D,D,N,R S,D,D,N,D

Here, you are the second bot (r,r,r,r,r), who tried to return all four servants that are still alive (and unluckily failed on all four). Bot 1's servants are all back in camp. Bot 3 has three dead servants, one more back in camp, and a fifth servant who successfully returned. Bot 4 has one servant who stayed (and will die, as this is the last turn of a day), one servant in camp, and three dead servants.

After each of these strings, unless a string signaling the end of the day has also been outputted (see below), your program is to output your servants' next moves, separated by commas. All servants must be accounted for (with N if already in camp, and D if already dead). Invalid moves will be treated as S if the servant is not already in camp/dead. Example:

N,N,S,S,R

which means:

Servant # | Action
     1    | Do nothing.
     2    | Do nothing.
     3    | Stay put (keep looking for treasure).
     4    | Stay put (keep looking for treasure).
     5    | Try to return to camp.

At the end of a day, the following string shall be passed after the last turn's END string, informing everyone on who is alive:

END_DAY [Day #] [Bot 1 Status] [Bot 2 Status] 

where the status is a comma separated list of either A (alive) or D (dead). The following day begins immediately after.

The simulation ends when there are fewer than 6 live servants. Your program will receive the following input at the end of the simulation:

EXIT

Rules/Details

  • Only on turns where your action is S will you find treasure.
  • Number of simulations run: 1000 times
  • Your program should not take any more than 1 second to determine moves.
  • Your program should not exit early; it will be started exactly once.
  • Make sure that the output buffer (if applicable) is flushed after each output.
  • Files may be written to in your bot's folder (./players/BotName/). Your bot name is whatever you name your bot, with all non-alphanumeric characters removed and written in CamelCase. Entries may save data between runs of the controller, as runs are done sequentially.
  • Your program must exit after receiving EXIT.
  • Programs that fail to compile or throw errors or output invalid text (not in the format of 5 characters separated by commas) may be excluded from the competition. A newline must follow each output.
  • The controller may be found on GitHub.

Please include the bot name, language+version, code, and command to compile (if applicable) and run your bot.

Example

Text outputted by the program is prefixed here with a >. Your program should not output this character.

INDEX 2
START_DAY 1/3
START_TURN 1
>S,S,S,S,S
END_TURN 1 S,R,S,S,S S,S,S,S,S
START_TURN 2
>S,S,S,S,S
END_TURN 2 S,N,S,R,S S,S,S,S,S
START_TURN 3
>R,R,S,S,S
END_TURN 3 R,N,R,N,R R,R,S,S,S
START_TURN 4
>N,N,S,S,S
END_TURN 4 N,N,N,N,N N,N,S,S,S
START_TURN 5
>N,N,R,R,R
END_TURN 5 N,N,N,N,N N,N,r,r,R
END_DAY 1 A,A,A,A,A A,A,D,D,A
START_DAY 2/3
START_TURN 1
>S,S,N,S,N
END_TURN 1 R,R,R,R,R S,S,D,D,N
END_DAY 2 A,A,A,A,A D,D,D,D,D
EXIT

The scores for the above example are:

Bot#    Day 1   Day 2   Total
1       10      0       10
  S1    1+2     0       3
  S2    0       0       0
  S3    1+2     0       3
  S4    1       0       1
  S5    1+2     0       3

2       20      0       20
  S1    1+2     0       3
  S2    1+2     0       3
  S3    0       0       0
  S4    0       0       0
  S5    1+2+3+8 0       14

The winner is therefore the player, bot 2. Note that the winner does not have to survive to the absolute end. (Also note that the player could have remained until turn 30 on day 1, since the camp would not be full until the player sent one more bot back).

Scores

Bot               Score
Bob               2939.422
Statisticians     2905.833
Morning Birds     1652.325
Evolved           1578.285
Slow Returners    1224.318
Wandering Fools   1065.908
Randomizers       735.313
Drunkards         0     
Plague            0

Logs are available on GitHub. Results per each trial are available on this google spreadsheet.

es1024

Posted 2014-11-13T23:09:27.163

Reputation: 8 953

If a servant doesn't return, is he counted towards the number of people that die this day? – EagleV_Attnam – 2014-11-14T11:14:14.970

@EagleV_Attnam The day ends either when there are enough servants that have returned, or 30 turns have passed, in which everyone that hasn't returned will die, regardless of the earlier determined number of deaths. – es1024 – 2014-11-14T15:27:49.793

Right, that was silly, sorry. – EagleV_Attnam – 2014-11-14T15:39:19.053

If a servant returns to camp, can he deliver treasure found so far and then go out to search again on that same day? – Logic Knight – 2014-11-15T01:46:28.510

1@MikeSweeney No. Once a servant returns, he stays. – es1024 – 2014-11-15T02:12:25.570

Do I understand correctly that the number of competing bots in a simulation is unknown until after the first turn of the first day? – Zgarb – 2014-11-15T18:32:06.150

@Zgarb If there are enough bots (4 or more), the number of competing bots can be guessed from the maximum number of servants that may die, passed with START_DAY. Otherwise, yes, the number is unknown until after the first turn. – es1024 – 2014-11-15T22:19:40.297

Hold off running the competition until Tuesday, OK? I've got a bot in the works, but it's taking some time. ;) – COTO – 2014-11-17T01:45:40.500

@COTO The run that determines the bounty will be done during the grace period of the bounty, so you have plenty of time. – es1024 – 2014-11-17T02:21:02.690

Darn it. I need a few more hours. Not going to make the deadline. >___< – COTO – 2014-11-22T21:04:45.100

@COTO I'll probably be running the controller around 12 hours from now – es1024 – 2014-11-22T23:03:27.847

@es1024: Thanks for the heads-up. I unfortunately just don't have the time. Work obligations and all. I might still submit a bot "for the heck of it" somewhere down the line, however. Best of luck to all participants. ;) – COTO – 2014-11-23T02:49:23.407

Answers

5

Bob - C++

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>

using namespace std;

int compare(int i, int j)
  {
  if (i < j)
    return (-1);
  if (i == j)
    return (0);
  if (i > j)
    return (1);
  }

int main()
  {
  int index;
  int day;
  int turn;
  int slash_index;
  int to_die;
  int num_alive;
  int mine_alive;
  int turn_to_return;
  bool returned;
  string line;
  vector<int> last_returns;
  vector<int> today_returns;

  getline(cin, line);

  if (line.compare(0, 6, "INDEX ") != 0)
    {
    cerr << "INVALID INDEX LINE \"" << line << "\"" << endl;

    return (-1);
    }

  index = atoi(line.substr(6).c_str()) - 1;

  while (1) // Day loop
    {
    getline(cin, line);
    if (line.compare(0, 4, "EXIT") == 0)
      {
      return (0);
      }
    else if (line.compare(0, 9, "START_DAY") != 0 || (slash_index = line.find('/')) == string::npos)
      {
      cerr << "INVALID START_DAY \"" << line << "\"" << endl;
      return (-1);
      }

    day = atoi(line.substr(10, slash_index - 10).c_str());
    to_die = atoi(line.substr(slash_index + 1, line.length() - slash_index - 1).c_str());

    if (day != 1)
      {
      if (to_die > num_alive)
        {
        turn_to_return = 30;
        }
      else
        {
        turn_to_return = last_returns[last_returns.size() - to_die] - 1;
        }
      }

    returned = false;

    for (turn = 1; turn <= 30; ++turn)
      {
      getline(cin, line);

      if (line.compare(0, 4, "EXIT") == 0)
        {
        return (0);
        }
      if (line.compare(0, 7, "END_DAY") == 0)
        {
        goto end_day;
        }
      if (line.compare(0, 10, "START_TURN") != 0)
        {
        cerr << "INVALID START_TURN \"" << line << "\"" << endl;
        }

      if (day == 1)
        {
        switch (compare(turn, 30))
          {
            case -1:
              cout << "S,S,S,S,S" << endl;
              break;

            case 0:
              cout << "R,R,R,R,R" << endl;
              break;

            case 1:
              cout << "N,N,N,N,N" << endl;
              break;
          }
        }
      else
        {
        if (returned)
          {
          cout << "N,N,N,N,N" << endl;
          }
        /*
        else if (num_alive - today_returns.size() < to_die)
          {
          cout << "R,R,R,R,R" << endl;
          returned = true;
          }
        */
        else if (turn >= turn_to_return)
          {
          cout << "R,R,R,R,R" << endl;
          returned = true;
          }
        else
          {
          cout << "S,S,S,S,S" << endl;
          }
        }

      getline(cin, line);

      if (line.compare(0, 4, "EXIT") == 0)
        {
        return (0);
        }
      if (line.compare(0, 8, "END_TURN") != 0)
        {
        cerr << "INVALID END_TURN \"" << line << "\"" << endl;
        }

      stringstream ss(line);
      string item;
      int i = 0;
      while (getline(ss, item, ' '))
        {
        i++;
        if (i > 2 && i - 3 != index)
          {
          int num_to_add = count(item.begin(), item.end(), 'R'); // Add turn to today_returns for each servant that returned
          for (int j = 0; j < num_to_add; j++)
            {
            today_returns.push_back(turn);
            }
          }
        }

      }

    getline(cin, line);

  end_day:

    if (line.compare(0, 4, "EXIT") == 0)
      {
      return (0);
      }
    else if (line.compare(0, 7, "END_DAY") != 0)
      {
      cerr << "INVALID END_DAY \"" << line << "\"" << endl;
      return (-1);
      }

    stringstream ss(line);
    string item;
    int i = 0;
    num_alive = 0;
    while (getline(ss, item, ' '))
      {
      i++;
      if (i > 2 && i - 3 != index)
        {
        num_alive += count(item.begin(), item.end(), 'A');
        }
      else if (i - 3 == index)
        {
        mine_alive = count(item.begin(), item.end(), 'A');
        }
      }

    last_returns = today_returns;
    today_returns.clear();

    }

  return (0);
  }

To Compile:

g++ -o Bob.exe Bob.cpp

To Run:

./players/Bob/Bob.exe

user31611

Posted 2014-11-13T23:09:27.163

Reputation:

6

Statisticians, Python 3

The statisticians always work together. On the first turn, they return to the camp when two thirds of their opponents have done so. On the subsequent turns, they rely on the data they have collected from the previous turns to predict the habits of the other servants, and try to return to the camp at the last safe moment.

Program

# Team of treasure-hunting statisticians
# Run with:
# python3 statisticians.py

num_others = None
running = True
while running:
    msg = input().split()
    msg_type = msg.pop(0)
    if msg_type == "INDEX":
        my_index = int(msg[0])-1
    elif msg_type == "START_DAY":
        day, max_deaths = tuple(map(int, msg[0].split('/')))
    elif msg_type == "START_TURN":
        turn = int(msg[0])
        if day == 1:
            if turn == 1:
                print("S,S,S,S,S")
            elif turn == 30 or num_active <= max_deaths * 4/5:
                print("R,R,R,R,R") # On first day, return when 4/5 of  maximum number of dying servants remain
            else:
                print("S,S,S,S,S")
        elif turn >= 29 or len(expected_servants[turn+1]) <= max(2, max_deaths * 3/4) or len(expected_servants[turn]) <= max(2, max_deaths * 1/4):
            print("R,R,R,R,R") # If many servants are expected to return next turn or someone is sure to die, return to camp
        else:
            print("S,S,S,S,S") # Otherwise, keep going
    elif msg_type == "END_TURN":
        turn = int(msg.pop(0))
        others_moves = [tuple(s.split(',')) for s in msg[:my_index] + msg[my_index+1:]]
        if num_others is None: # End of first turn, initialize variables that depend on number of servants
            num_others = len(others_moves)
            others_history = [{} for i in range(num_others)]
        if day == 1:
            num_active = sum([move.count('S') for move in others_moves])
        for i, moves in enumerate(others_moves): # Log the return habits of other bots
            if turn == 1:
                others_history[i][day] = [0]*5
            for j, move in enumerate(moves):
                if move == "R": # Only safely returned servants are taken into account
                    others_history[i][day][j] = turn
                    if day > 1:
                        for future_turn in range(turn, 30):
                            expected_servants[future_turn].discard((i,j))
    elif msg_type == "END_DAY":
        day = int(msg.pop(0))
        my_statuses = tuple(msg[my_index].split(','))
        others_statuses = [tuple(s.split(',')) for s in msg[:my_index] + msg[my_index+1:]]
        expected_servants = [set() for i in range(30)] # Compute the sets of expected servants for each turn
        for i in range(num_others):
            for j in range(5):
                if others_statuses[i][j] == 'A':
                    turn_sum = 0
                    for day_num in others_history[i]:
                        turn_sum += others_history[i][day_num][j]
                    for turn in range(turn_sum//day):
                        expected_servants[turn].add((i,j))
    elif msg_type == "EXIT":
        running = False

As you can see, I shamelessly stole the program structure from @Mike Sweeney.

Command

python3 statisticians.py

EDIT: Fixed a bug in the check for returning home. They should perform somewhat better now.

EDIT 2: The statisticians are now smarter than before: they keep track of which servants have returned to the camp in the current day, and adjust their predictions accordingly. Also, they take more risks, returning to the camp when 3/4 of the maximum number of dying servants remain. This pushes them back to the top (just barely; Bob has become very dangerous).

Zgarb

Posted 2014-11-13T23:09:27.163

Reputation: 39 083

5

Drunkards, Perl 5

A bit too much alcohol and they'll never find their way back to camp.

This entry is primarily an example, but will participate.

Program

#!/usr/bin/perl
use 5.10.1;
$| = 1; # disable buffering
@actions = qw(S S S S S);

$_ = <>; ~/^INDEX (\d+)/;
$index = $1;

while(<>){
    if(index($_, 'START_TURN') == 0){
        say join(',', @actions);
    }elsif(index($_, 'END_DAY') == 0){ 
        # update actions based on who is alive
        # index 1-indexed; first bot at position 2.
        # this is not actually necessary for Drunkards, as all of Drunkards'
        #  servants will die on day 1 in any case.
        # This check is here simply as an example.
        my @status = split(',',(split(' '))[$index + 1]);
        my $i;
        for($i = 0; $i < 5; ++$i){
            # action is S if alive, N if dead. Servants will never be in camp.
            $actions[$i] = $status[$i] eq 'A' ? 'S' : 'N';
        }
    }elsif(index($_, 'EXIT') == 0){
        exit 0;
    }
}

Command

perl ./players/Drunkards/Drunkards.pl

es1024

Posted 2014-11-13T23:09:27.163

Reputation: 8 953

Should your code $status[$i] eq 'A' ? 'S' : 'D'; be $status[$i] eq 'A' ? 'S' : 'N'; to meet the spec? – Logic Knight – 2014-11-15T01:25:00.670

@MikeSweeney Good catch. I forgot to fix that when I changed the spec while this challenge was still in the sandbox. – es1024 – 2014-11-15T02:14:38.253

4

Morning Birds

The early bird catches the worm!!!

package players.MorningBirds;

import java.io.*;
import java.util.*;

/*
 * Java 7
 * 
 * Compile with "javac ./players/MorningBirds/MorningBirds.java"
 * Run with "java players.MorningBirds.MorningBirds"
 * 
 * Servants find treasure from morning until noon.
 * At noon they go to bed to prepare for next day.
 * 
 * According to Benjamin Franklin, "Early to bed, early to rise, keeps a 
 *      man healthy, WEALTHY, and wise."
 * 
 * 
 */
public class MorningBirds {

    protected final static String STARTDAY = "START_DAY";

    protected final static String STARTTURN = "START_TURN";

    protected final static String ENDTURN = "END_TURN";

    protected final static String ENDDAY = "END_DAY";

    protected final static String MOVERETURN = "R";

    protected final static String MOVESEARCH = "S";

    protected final static String MOVENOTHING = "N";

    protected final static String RETURNED = "R";

    protected final static String FAILEDRETURN = "r";

    protected final static String SEARCHING = "S";

    protected final static String DEAD = "D";

    protected final static String SLEEPING = "N";

    protected final static String EXIT = "EXIT";

    protected final static String ALIVE = "A";

    protected enum Status{SEARCHING, DEAD, RETURNED}

    protected enum Move{RETURN, SEARCH, NOTHING}

    protected int index;

    protected int day;

    protected int turnNum;

    protected int howManyTeams;

    protected int howManyWillDieTodayAtMost;

    protected int howManyHaveDiedToday;

    protected int howManyEnemyPlayers;

    protected int howManyAliveEnemyPlayers;

    protected int howManyEnemyTeams;

    protected int howManyDeadEnemyPlayers;

    protected int howManyReturnedEnemyPlayers;

    protected int howManySearchingEnemyPlayers;

    protected int howManyTotalPlayers;

    protected int howManyTotalAlivePlayers;

    protected int howManyTotalDeadPlayers;

    protected int howManyTotalReturnedPlayers;

    protected int howManyTotalSearchingPlayers;

    protected int howManyOwnAlivePlayers;

    protected int howManyOwnDeadPlayers;

    protected int howManyOwnReturnedPlayers;

    protected int howManyOwnSearchingPlayers;

    protected Status[] statuses = new Status[5];

    protected Status[][] allStatuses = null;

    protected List<Status[][]> allDayStatuses = null;

    protected List<List<Status[][]>> allTimeStatuses = new ArrayList<>();

    protected BufferedReader in = new BufferedReader(
            new InputStreamReader(System.in));

    public static void main (String args[]) throws Exception{
        new MorningBirds().start();
    }

    public void start() throws Exception{

        index = Integer.parseInt(in.readLine().split("\\s")[1]);
        Arrays.fill(statuses, Status.SEARCHING);

        while(true){
            String[] input = in.readLine().split("\\s");
            if (input[0].equals(ENDTURN) || input[0].equals(ENDDAY)){
                updateStatus(input);
            } else if (input[0].equals(EXIT)){
                return;
            } else if (input[0].equals(STARTDAY)){
                updateDay(input);
            } else if (input[0].equals(STARTTURN)){
                updateTurn(input);
                doTurn(input);
            }

        }

    }

    protected void updateStatus(String[] input){
        if (allStatuses == null && input[0].equals(ENDTURN)){
            allStatuses = new Status[input.length - 2][5];
            for (Status[] enemyStatus : allStatuses){
                Arrays.fill(enemyStatus, Status.SEARCHING);
            }
            howManyTeams = input.length - 2;
            howManyEnemyTeams = input.length - 3;
            howManyTotalPlayers = howManyTeams * 5;
            howManyTotalAlivePlayers = howManyTotalPlayers;
            howManyTotalSearchingPlayers = howManyTotalAlivePlayers;
            howManyAliveEnemyPlayers = howManyTotalPlayers - 5;
            howManyEnemyPlayers = howManyEnemyTeams * 5;
            howManyOwnAlivePlayers = 5;
            howManyOwnSearchingPlayers = 5;
            howManySearchingEnemyPlayers = howManyAliveEnemyPlayers;
        }
        for ( int j = 0; j < howManyTeams; j++){
            String[] stats = input[j + 2].split(",");
            for(int i = 0; i < 5; i++){
                switch (stats[i]){
                    case "R":
                    case "N":
                        if (allStatuses[j][i] != Status.RETURNED){
                            howManyTotalReturnedPlayers++;
                            howManyTotalSearchingPlayers--;
                            if (j == index - 1) {
                                howManyOwnReturnedPlayers++;
                                howManyOwnSearchingPlayers--;
                            } else {
                                howManyReturnedEnemyPlayers++;
                                howManySearchingEnemyPlayers--;
                            }
                        }
                        allStatuses[j][i] = Status.RETURNED;
                        break;
                    case "A":
                    case "S":
                        if (allStatuses[j][i] != Status.SEARCHING){
                            howManyTotalReturnedPlayers--;
                            howManyTotalSearchingPlayers++;
                            if (j == index - 1) {
                                howManyOwnReturnedPlayers--;
                                howManyOwnSearchingPlayers++;
                            } else {
                                howManyReturnedEnemyPlayers--;
                                howManySearchingEnemyPlayers++;
                            }
                        }
                        allStatuses[j][i] = Status.SEARCHING;
                        break;
                    case "r":
                    case "D":
                        if (allStatuses[j][i] != Status.DEAD){
                            howManyTotalAlivePlayers--;
                            howManyTotalDeadPlayers++;
                            howManyHaveDiedToday++;
                            howManyTotalSearchingPlayers--;
                            if (j == index - 1){
                                howManyOwnAlivePlayers--;
                                howManyOwnDeadPlayers++;
                                howManyOwnSearchingPlayers--;
                            } else {
                                howManyAliveEnemyPlayers--;
                                howManyDeadEnemyPlayers++;
                                howManySearchingEnemyPlayers--;
                            }
                        }
                        allStatuses[j][i] = Status.DEAD;
                        break;
                    default:
                        break;
                }
            }
        }
        statuses = allStatuses[index - 1];
        if (input[0].equals(ENDTURN)){
            allDayStatuses.add(allStatuses.clone());
        }
        if (input[0].equals(ENDDAY)){
            Status[][] statusesToAdd = new Status[howManyTeams][5];
            for (int i = 0; i < statusesToAdd.length; i++){
                for (int j = 0; j < statusesToAdd[i].length; j++){
                    if (allStatuses[i][j] == Status.SEARCHING){
                        statusesToAdd[i][j] = Status.RETURNED;
                    } else {
                        statusesToAdd[i][j] = Status.DEAD;
                    }
                }
            }
            while (turnNum <= 30){
                allDayStatuses.add(statusesToAdd.clone());
                turnNum++;
            }
            allTimeStatuses.add(allDayStatuses);
        }
    }

    protected void updateDay(String[] input) throws Exception{
        day = Integer.parseInt(input[1].split("/")[0]);
        howManyWillDieTodayAtMost = Integer.parseInt(input[1].split("/")[1]);
        howManyHaveDiedToday = 0;
        allDayStatuses = new ArrayList<>();
        if (day == 1){
            Arrays.fill(statuses, Status.SEARCHING);
            howManyOwnAlivePlayers = 5;
            howManyOwnSearchingPlayers = 5;
        }
    }

    protected void updateTurn(String[] input){
        turnNum = Integer.parseInt(input[1]);
    }

    protected void doTurn(String[] input){
        Move[] moves = new Move[5];
        for (int i = 0; i < 5; i++){
            if (statuses[i] == Status.DEAD ||
                        statuses[i] == Status.RETURNED) {
                moves[i] = Move.NOTHING;
                continue;
            } else {
                moves[i] = doMove(i);
            }
        }
        String[] outputs = new String[5];
        for (int i = 0; i < 5; i++){
            switch (moves[i]){
                case SEARCH:
                    outputs[i] = MOVESEARCH;
                    break;
                case RETURN:
                    outputs[i] = MOVERETURN;
                    break;
                case NOTHING:
                    outputs[i] = MOVENOTHING;
            }
        }
        String totalOutput = "";
        for(String output : outputs){
            if (totalOutput != ""){
                totalOutput += ",";
            }
            totalOutput += output;
        }
         System.out.println(totalOutput);
    }

    //Implement this method differently for different 
    //strategies. 
    public Move doMove(int playerNumber){
        if (turnNum >= 15){
            return Move.RETURN;
        }
        return Move.SEARCH;
    }

    /**
     * Returns the status of one of your players. 
     * Your players have numbers 1 to 5 inclusive.
     * Throws exception if number is outside range.
     * 
     */
    protected Status getStatus(int player){
        if (player > 5 || player < 1){
            throw new IllegalArgumentException(
                    "getStatus(" + player +") failed.");
        }
        return statuses[player - 1];
    }

    /**
     * Returns the status of a player in a team.
     * Team numbers start with 1 inclusive.
     * Players have numbers 1 to 5 inclusive.
     * Throws exception if argument player is outside range.
     * Throws exception if argument team is less than 1 or is greater
     * than the number of teams.
     * Returns Status.SEARCHING if day == 1 and turnNum == 1 and argument 
     * team >= 1.
     */
    protected Status getStatus(int team, int player){
        if (team < 1 || player < 1 || player > 1 || 
                (team > howManyTeams && day == 1 && turnNum == 1)){
            throw new IllegalArgumentException(
                    "getStatus(" + team + ", " + player + ") failed.");
        }
        if (day == 1 && turnNum == 1 && team >= 1){
            return Status.SEARCHING;
        }
        return allStatuses[team - 1][player - 1];
    }

    /**
     * Returns the status of a player in a team at the end of argument
     * turn.
     * Team numbers start with 1 inclusive.
     * Players have numbers 1 to 5 inclusive.
     * Turns have numbers 0 to 30 inclusive.
     * Status at turn 0 is equal to status at start of turn 1.
     * Throws exception if argument turn hasn't happened yet.
     * Throws exception if argument player is outside range.
     * Throws exception if argument team is less than 1 or is greater
     * than the number of teams.
     */
    protected Status getStatus(int turn, int team, int player){
        if (turn == 0){
            if (day == 1){
                return Status.SEARCHING;
            } else {
                return getStatus(day - 1, 30, team, player);
            }
        }
        if (turnNum <= turn || turn < 0|| player > 5 || player < 1 ||
                team < 1 || team > howManyTeams){
            throw new IllegalArgumentException("getStatus(" + turn + 
                    ", " + team + ", " + player + ") failed.");
        }
        return allDayStatuses.get(turn - 1)[team - 1][player - 1];
    }

    /**
     * Returns the status of a player in a team at the end of argument
     * turn on the day of argument day.
     * Team numbers start with 1 inclusive.
     * Players have numbers 1 to 5 inclusive.
     * Turns have numbers 0 to 30 inclusive.
     * Days have numbers 1 inclusive and up.
     * Status at turn 0 is equal to status at start of turn 1.
     * Throws exception if argument day hasn't ended yet or is less 
     * than one.
     * Throws exception if argument turn is out of range.
     * Throws exception if argument player is outside range.
     * Throws exception if argument team is less than 1 or is greater
     * than the number of teams.
     */
    protected Status getStatus(int day, int turn, int team, int player){
        if (turn == 0){
            if (day == 1){
                return Status.SEARCHING;
            } else {
                return getStatus(day - 1, 30, team, player);
            }
        }
        if (this.day <= day || day < 1 || turn > 30 || turn < 0 || 
                player > 5 || player < 1 ||
                team < 1 || team > howManyTeams){
            throw new IllegalArgumentException("getStatus(" + day + ", "
                    + turn + ", " + team + ", " + player + ") failed.");
        }
        return allTimeStatuses.get(day - 1).get(turn - 1)[team - 1][player - 1];
    }

}

Edit: Made it so anyone could easily subclass it. Just redefine doMove(int playerNumber) for your own bot. I have added several helpful fields and methods. I have extensively tested it. It does not save the statuses from previous simulations. Please tell me if there are any problems.

Compile with: javac ./players/MorningBirds/MorningBirds.java

Run with: java players.MorningBirds.MorningBirds

TheNumberOne

Posted 2014-11-13T23:09:27.163

Reputation: 10 855

Would it be okay if I made the methods and variables protected and later made a subclass of this for the challenge? – TheNumberOne – 2014-11-16T14:10:03.097

Feel free to use multiple source files if necessary, or reuse code from other entries, as long as entries are not working together. – es1024 – 2014-11-16T23:34:26.550

@es1024 In experimenting I noticed that a bot dies if it does nothing all day from turn 1. Is that intended? – TheNumberOne – 2014-11-18T02:19:18.143

A bot that never returns (R) on any given day will always die that day. – es1024 – 2014-11-18T03:16:39.307

The controller becomes unresponsive if I add players SlowReturners and Randomizers. Note: Sorry I'm posting comments here. I do not have the reputation needed to post elsewhere. – TheNumberOne – 2014-11-19T21:49:46.120

That's likely because the ruby solutions don't output a newline or flush the buffer as they need to; I've added a comment about this on those solutions. – es1024 – 2014-11-19T23:42:59.400

3

Randomizers - Ruby

Just to mess up statistics driven bots, the randomizers are quite unpredictable. All of them do return at once, at a random turn in an attempt to strand others.

(Not influenced by other players.)

def min(a,b);(a<b)?a:b;end
x=""
r=0
while x != "EXIT"
  x=gets.chomp
  if x =~ /^START_DAY/
    r = min(rand(30),rand(30))
  end
  if x =~ /^START_TURN (\d*)/
    puts ($1.to_i>r)?'R,R,R,R,R':'S,S,S,S,S'
  end
end

MegaTom

Posted 2014-11-13T23:09:27.163

Reputation: 3 787

2

Wandering Fools, Python 2

This is a simple Python bot that sends out the servants until a preset "goback" time is reached, then they try to enter camp and stay until the next day.

It is also a basic framework for more complex bots that others may wish to use. It is, however, untested with the judge engine, so let me know if I have made an error.

Program

import sys
from random import randint, choice
team = range(5)

while True:
    inp = sys.stdin.readline().split()
    cmd = inp.pop(0)
    if cmd == 'INDEX':
        teamnum = int(inp[0]) - 1   # using zero based indexing
    elif cmd == 'START_DAY':
        daynum, deadnum = [int(v) for v in inp[0].split('/')]
        # Set up strategy for the day:
        goback = [randint(5,25) for i in team]
    elif cmd == 'START_TURN':
        turn = int(inp[0])
        # Output actions [R]eturn, [S]earch, [N]othing here:
        actions = ['S' if turn < goback[i] else 'R' for i in team]
        sys.stdout.write( (','.join(actions)) + '\n' )
        sys.stdout.flush()
    elif cmd == 'END_TURN':
        endturn = int(inp.pop(0))
        status = [v.split(',') for v in inp]  # R,r,S,D,N
        # [R]eturned, [r]ejected, [S]earching, [D]ead, [N]othing
        mystatus = status[teamnum]
    elif cmd == 'END_DAY':
        endturn = int(inp.pop(0))
        alive = [v.split(',') for v in inp]  # [A]live or [D]ead
        myalive = alive[teamnum]
    elif cmd == 'EXIT':
        sys.exit(0)

Command

python WanderingFools.py

Edit: Changed action deciding code after rule clarification.

Logic Knight

Posted 2014-11-13T23:09:27.163

Reputation: 6 622

2

Evolved

I used Genetic Programming (through JGAP) to make this bot. It came up with a simple answer that beats all others (barely).

package players.Evolved;

import players.MorningBirds.*;
import java.util.*;

public class Evolved extends MorningBirds{

    List<Integer> scrambled = new ArrayList<>();

    public static void main(String[] args) throws Exception{
        new Evolved().start();
    }

    public Evolved() throws Exception{
        super();
    }

    @Override
    public MorningBirds.Move doMove(int playerNum){
        if (!(howManyTotalSearchingPlayers < (turnNum - getScrambled(index)))){
            return Move.SEARCH;
        } else {
            return Move.RETURN;
        }
    }

    @Override
    protected void updateStatus(String[] input){
        super.updateStatus(input);
        if (input[0].equals(ENDTURN) && (Integer.parseInt(input[1]) == 1)){
            for (int i = 1; i <= howManyTeams; i++){
                scrambled.add(i);
            }
            Collections.shuffle(scrambled);
        }
    } 

    public int getScrambled(int in){
        if (in > scrambled.size() || in < 1 ){
            return in;
        }
        return scrambled.get(in - 1);
    }
}

Compile with: javac players/Evolved/Evolved.java

Run with: java players.Evolved.Evolved

Edit: Grrr... Bob messed me up!!!

Edit: Yay!!! Bob, got killed by a nasty plague!!!

TheNumberOne

Posted 2014-11-13T23:09:27.163

Reputation: 10 855

1

SlowReturners - Ruby

Sends one servant back every 5 turns.

x=""
while x != "EXIT"
  x=gets.chomp
  if x =~ /^START_TURN (\d*)/
    puts (1..5).map{|i|(i<=$1.to_i/5)?"R":"S"}.join(",")
  end
end

MegaTom

Posted 2014-11-13T23:09:27.163

Reputation: 3 787

1

Plague

Plague is a disease. It is not rational. It is predictable. Diseases cannot collect treasure, nor do they care for treasure. Plague makes other players sick. The wise ones stay home and forget about treasure. The foolish ones are always foolish and will never get much treasure. Evolved is (fortunately) immune to Plague. He is also wise. He goes and collects treasure, and does not die.

package players.Plague;

import players.MorningBirds.MorningBirds;

public class Plague extends MorningBirds{

    public static void main(String[] args) throws Exception{
        new Plague().start();
    }

    public Plague() throws Exception{
        super();
    }

    @Override
    public MorningBirds.Move doMove(int playerNum){
        if (day > howManyTotalDeadPlayers){
            return Move.SEARCH;
        } else {
            return Move.RETURN;
        }
    }
}

Compile with: javac players/Plague/Plague.java

Run with: java players.Plague.Plague

Bob and Statisticians are now resistant to plague.

TheNumberOne

Posted 2014-11-13T23:09:27.163

Reputation: 10 855

hmm...when I run this bot it always dies on the first day... – None – 2014-11-22T13:59:03.110

I used a genetic algorithm to make this. It should die on the second day. It messes up statistic driven bots so that they perform quite badly compared to Evolved. – TheNumberOne – 2014-11-22T15:16:33.290