# Hot Potato Salesman

Given a list of points, find the shortest path that visits all points and returns to the starting point.

The Travelling Salesman Problem is well-known in the computer science field, as are many ways to calculate/approximate it. It's been solved for very large groups of points, but some of the largest take many CPU-years to finish.

Don't get burned by the potato.

Hot Potato is a game where 2+ players pass a "potato" around in a circle while music plays. The object is to pass it to the next player quickly. If you are holding the potato when the music stops, you're out.

The object of Hot Potato Salesman is:

Given a set of 100 unique points, return those points in a better order (shorter total distance as defined further down). This will "pass" the problem to the next player. They have to improve it and pass it to the next, etc. If a player cannot improve it, they are out and play continues until one player is left.

To keep this from being a "brute-force-me-a-path" competition, there are these stipulations:

• You cannot take more than one minute to pass the potato. If you haven't found and passed a shorter solution by the time one minute is up, you're out.

• You cannot change the position of more than 25 points. To be exact, >= 75 points must be in the same position as you received them. It does not matter which ones you decide to change, just the amount you change.

When only one player is left, he is the winner of that game, and receives one point. A tourney consists of 5*n games, where n is the number of players. Each game, the starting player will be rotated, and the remaining player order will be shuffled. The player with the most points at the end is the winner of the tourney. If the tourney ends with a tie for first place, a new tourney will be played with only those contestants. This will continue until there is no tie.

The starting player for each game will receive a set of pseudorandomly selected points in no particular order.

Points are defined as a pair of integer x,y coordinates on a cartesian grid. Distance is measured using Manhattan distance, |x1-x2| + |y1-y2|. All coordinates will lie in the [0..199] range.

## Input

Input is given with a single string argument. It will consist of 201 comma-separated integers representing the number of current players(m) and 100 points:

m,x0,y0,x1,y1,x2,y2,...,x99,y99


The order of these points is the current path. The total distance is obtained by adding the distance from each point to the next (dist(0,1) + dist(1,2) + ... + dist(99,0)). Don't forget to return to start when calculating total distance!

Note that m is not the number of players that started the game, it is the number that are still in.

## Output

Output is given in the same way as input, minus m; a single string containing comma-separated integers representing the points in their new order.

x0,y0,x1,y1,x2,y2,...,x99,y99


The control program will wait for output for one minute only. When output is received, it will verify that:

• the output is well-formed
• the output consists of only and all the 100 points present in the input
• >=75 points are in their original positions
• the path length is less than the previous path

If any of these checks fail (or there is no output), you are out and the game will proceed to the next player.

## Control Program

You can find the control program at this link. The control program itself is deterministic, and is posted with a dummy seed of 1. The seed used during scoring will be different, so don't bother trying to analyze the turn order/point lists it spits out.

The main class is Tourney. Running this will do a full tournament with contestants given as arguments. It spits out the winner of each game and a tally at the end. A sample tourney with two SwapBots looks like:

Starting tournament with seed 1

(0) SwapBot wins a game! Current score: 1
(1) SwapBot wins a game! Current score: 1
(1) SwapBot wins a game! Current score: 2
(1) SwapBot wins a game! Current score: 3
(0) SwapBot wins a game! Current score: 2
(1) SwapBot wins a game! Current score: 4
(1) SwapBot wins a game! Current score: 5
(1) SwapBot wins a game! Current score: 6
(1) SwapBot wins a game! Current score: 7
(1) SwapBot wins a game! Current score: 8

Final Results:

Wins        Contestant
2       (0) SwapBot
8       (1) SwapBot


If you want to test just one game at a time, you can run the Game class instead. This will run one game with players in the order given as arguments. By default, it will also print a play-by-play showing the current player and path length.

Also included are a few test players: SwapBot, BlockPermuter, and TwoSwapBot. The first two will not be included in scoring runs, so feel free to use and abuse them during testing. TwoSwapBot will be included in judging, and he's no slouch, so bring your A-game.

## Miscellany

• You cannot save state information, and each turn is a separate run of your program. The only information you will receive each turn is the set of points.

• You cannot use external resources. This includes network calls and file access.

• You cannot use library functions designed to solve/assist with the TSP problem or its variants.

• You cannot manipulate or interfere with other players in any way.

• You cannot manipulate or interfere with the control program or any included classes or files in any way.

• One submission per user. If you submit more than one entry, I will only enter the first one submitted. If you want to change your submission, edit/delete the original.

• The tournament will be running on Ubuntu 13.04, on a computer with an i7-3770K CPU and 16GB RAM. It will not be run in a VM. Anything I perceive as malicious will immediately disqualify the current and any future entry you submit.

• All entries must be runnable from the command line with free (as in beer) software. If I have problems compiling/running your entry, I will request aid in the comments. If you do not respond or I cannot ultimately get it running, it will be disqualified.

## Results (22 May 2014)

New results are in! UntangleBot has beaten the competition quite soundly. TwoSwapBot managed seven wins, and SANNbot snuck a victory in as well. Here's a scoreboard, and a link to the raw output:

Wins        Contestant
22      (2) ./UntangleBot
7       (0) TwoSwapBot
1       (5) SANNbot.R
0       (1) BozoBot
0       (4) DivideAndConquer


As it stands now, UntangleBot has won the checkmark. Don't let that discourage you from entering, though, since I'll run the tournament as more contestants appear and change the accepted answer accordingly.

• Comments purged. Please notify me for any possible lost information. – Doorknob May 23 '14 at 12:14
• Man why did this challenge have to be during my final exams (finally, done with school yeay). You sure are a bad planner Geobits ;) Weird/sadly enough at that time there were tons of king-of-the-hill questions and now there aren't any (maybe it works better if there is only one at a time, hint hint) ... – Herjan May 28 '14 at 16:16
• @Herjan Feel free to try to tackle the current champ. I'll run the tourney again as new contestants appear, so the contest isn't over or anything. Beat SirDarius and it may spur him or someone else to beat yours, breathing life back into it ;) – Geobits May 28 '14 at 16:19
• @Herjan Please do submit an entry ! I believe there is a lot of room for improvement here. Most of the solutions here, including mine, do not rely on clever algorithms specific to this problem. – SirDarius May 29 '14 at 17:57
• I think there is a problem with modification limitation. As some times it will take to modify the entire set of data to get better solution. – Ilya Gazman Jul 1 '14 at 21:16

# UntangleBot (formerly NiceBot)

A C++11 bot that uses two strategies.
At first it will try to "untangle" the path if possible, by detecting intersections between paths that are closer than 25 points (since untangling implies modifying all points in-between).
If the first strategy fails, it randomly swaps points to find better distances until a better path has been found.

This bot consistently beats TwoSwapBot with an approximate ratio of five victories for one loss in my test tourneys.

// g++ -std=c++11 -O3 -o UntangleBot UntangleBot.cpp
#include <algorithm>
#include <chrono>
#include <cmath>
#include <iostream>
#include <iterator>
#include <random>
#include <set>
#include <sstream>

const int NPOINTS = 100;

struct Point {
int x, y;

Point():x(0),y(0) {}
Point(int x, int y):x(x),y(y) {}

int distance_to(const Point & pt) const {
return std::abs(x - pt.x) + std::abs(y - pt.y);
}
};

std::ostream & operator<<(std::ostream & out, const Point & point) {
return out << point.x << ',' << point.y;
}

int total_distance(const Point points[NPOINTS]) {
int distance = 0;
for (int i = 0; i < NPOINTS; ++i) {
distance += points[i].distance_to(points[(i+1)%NPOINTS]);
}
return distance;
}

bool intersects(const Point & p1, const Point & p2, const Point & p3, const Point & p4) {
double s1_x, s1_y, s2_x, s2_y;
s1_x = p2.x - p1.x;
s1_y = p2.y - p1.y;
s2_x = p4.x - p3.x;
s2_y = p4.y - p3.y;

double s, t;
s = (-s1_y * (p1.x - p3.x) + s1_x * (p1.y - p3.y)) / (-s2_x * s1_y + s1_x * s2_y);
t = ( s2_x * (p1.y - p3.y) - s2_y * (p1.x - p3.x)) / (-s2_x * s1_y + s1_x * s2_y);

return s >= 0 && s <= 1 && t >= 0 && t <= 1;
}

int main(int argc, char ** argv) {
Point points[NPOINTS];

using Clock = std::chrono::system_clock;
const Clock::time_point start_time = Clock::now();

// initialize points
if (argc < 2) {
std::cerr << "Point list is missing" << std::endl;
return 1;
}
std::stringstream in(argv[1]);
int players;
char v;
in >> players >> v;
for (int i = 0; i < NPOINTS; ++i) {
in >> points[i].x >> v >> points[i].y >> v;
}

int original_distance = total_distance(points);

// detect intersection between any 4 points
for (int i = 0; i < NPOINTS; ++i) {
for (int j = i+1; j < NPOINTS; ++j) {
Point & p1 = points[i];
Point & p2 = points[(i+1)%NPOINTS];
Point & p3 = points[j];
Point & p4 = points[(j+1)%NPOINTS];

// points must all be distinct
if (p1.distance_to(p3) == 0 || p1.distance_to(p4) == 0 || p2.distance_to(p3) == 0 || p2.distance_to(p4) == 0) {
continue;
}

// do they intersect ?
if (!intersects(p1, p2, p3, p4)) {
continue;
}

// can modify less than 25 points ?
if (std::abs(j-i) > 25) {
continue;
}

// swap points
for (int m = 0; m < std::abs(j-i)/2; ++m) {
if (i+1+m != j-m) {
std::swap(points[i+1+m], points[j-m]);
//std::cerr << "untangle " << i+1+m << " <=> " << j-m << '\n';
}
}

int new_distance = total_distance(points);
if (new_distance < original_distance) {
std::copy(std::begin(points), std::end(points)-1, std::ostream_iterator<Point>(std::cout, ","));
std::cout << points[NPOINTS-1];
return 0;
}
else {
// swap points back
for (int m = 0; m < std::abs(j-i)/2; m++) {
if (i+1+m != j-m) {
std::swap(points[i+1+m], points[j-m]);
}
}
}
}
}

// more traditional approach if the first fails
std::mt19937 rng(std::chrono::duration_cast<std::chrono::seconds>(start_time.time_since_epoch()).count());
std::uniform_int_distribution<> distr(0, NPOINTS-1);
while (true) {
// try all possible swaps from a random permutation
int p1 = distr(rng);
int p2 = distr(rng);
std::swap(points[p1], points[p2]);

for (int i = 0; i < NPOINTS; ++i) {
for (int j = i+1; j < NPOINTS; ++j) {
std::swap(points[i], points[j]);
if (total_distance(points) < original_distance) {
std::copy(std::begin(points), std::end(points)-1, std::ostream_iterator<Point>(std::cout, ","));
std::cout << points[NPOINTS-1];
return 0;
}
else {
std::swap(points[i], points[j]);
}
}
}

// they didn't yield a shorter path, swap the points back and try again
std::swap(points[p1], points[p2]);
}
return 0;
}

• You beat me by 19 minutes! – Rainbolt May 14 '14 at 16:17
• This should have more upvotes, judging from today's results. – Geobits May 16 '14 at 13:56
• @Geobits I'm still surprised the simplest thing I came up with performs so well. Hoping more challenging contestants will enter ! – SirDarius May 16 '14 at 13:58
• @SirDarius Okay, fine. Have a bit of challenge. – Geobits May 16 '14 at 18:25

### SANNbot

(a simulated annealing bot in R)

Should be called with Rscript SANNbot.R.

input <- strsplit(commandArgs(TRUE),split=",")[[1]]
n <- as.integer(input[1])                            # Number of players
init_s <- s <- matrix(as.integer(input[-1]),ncol=2,byrow=TRUE) # Sequence of points
totdist <- function(s){                              # Distance function
d <- 0
for(i in 1:99){d <- d + (abs(s[i,1]-s[i+1,1])+abs(s[i,2]-s[i+1,2]))}
d
}
gr <- function(s){                                   # Permutation function
changepoints <- sample(1:100,size=2, replace=FALSE)
tmp <- s[changepoints[1],]
s[changepoints[1],] <- s[changepoints[2],]
s[changepoints[2],] <- tmp
s
}
d <- init_d <- totdist(s)                            # Initial distance
k <- 1                                               # Number of iterations
v <- 0
t <- n*(init_d/12000)                                 # Temperature
repeat{
si <- gr(s)                                      # New sequence
di <- totdist(si)                                # New distance
dd <- di - d                                     # Difference of distances
if(dd <= 0 | runif(1) < exp(-dd/t)){             # Allow small uphill changes
d <- di
s <- si
v <- v+2
}
k <- k+1
if(k > 10000){break}
if(d > init_d & v>20){s <- init_s; d <- init_d; v <- 0}
if(v>23){break}
}
cat(paste(apply(s,1,paste,collapse=","),collapse=","))


The idea is relatively simple: each turn is one "cooling step" of a simulated annealing with the number of players still in the game as "temperature" (modified by the current distance over 12000, i.e. roughly the initial distance). Only difference with a proper simulated annealing is that I checked that i don't permute more than 25 elements and if i used up 20 moves but the resulting sequence is worth than the initial then it start over again.

• @Geobits ah sorry deleted the line where init_s was defined by accident (well "accident": i saw the line and thought "why is it here again?" and deleted it :) ). Corrected. – plannapus May 13 '14 at 14:49
• I tried your program using java Tourney "java Swapbot" "Rscript SANNbot.R" and it seemed to work. – plannapus May 13 '14 at 14:54
• Yea, it seems to be working now. – Geobits May 13 '14 at 14:55
• In theory (if I'm not completely mistaken) it should perform better when more players enter the game. – plannapus May 13 '14 at 15:04
• As-is, this program always goes out early due to "too many points changed" in my testing. If I bump the u check limits up, this doesn't happen (and it performs much better). While your code seems pretty straightforward, I don't know the quirks of R so I can't tell if the logic is wrong. (the latest version of the controller will give messages about why a bot goes out when running Game, so that might help pinpoint the problem) – Geobits May 14 '14 at 14:08

# BozoBot

Utilizes the complex logic behind Bozosort to find a better path. It looks like this.

• Swap random points
• If we improved
• Otherwise
• Try again

BozoBot has now been improved with Multithreading! Four minions now aimlessly juggle points until they stumble upon an improvement. The first one to find a solution gets a cookie!

import java.util.Random;
public class BozoBot {
public static String[] input;
public static void main(String[] args) {
input = args;
for(int i = 0; i < 4; i++) new Minion().run();
}
}

class Minion implements Runnable {
public static boolean completed = false;
public static synchronized void output(int[] x, int[] y) {
if(!completed) {
completed = true;
String result = x[0]+","+y[0];
for (int i = 1; i < 100; i++)
result+=","+x[i]+","+y[i];
System.out.print(result);
}
}
public void run() {
String[] args = BozoBot.input[0].split(",");
int[] x = new int[100];
int[] y = new int[100];
for (int i = 1; i < 201; i+=2) {
x[(i-1)/2] = Integer.parseInt(args[i]);
y[i/2] = Integer.parseInt(args[i+1]);
}
int startDistance = 0;
for (int i = 1; i < 100; i++)
startDistance += Math.abs(x[i-1]-x[i]) + Math.abs(y[i-1]-y[i]);
int r1, r2, r3, r4, tX, tY, newDistance;
Random gen = new java.util.Random();
while (true) {
r1 = gen.nextInt(100);
r2 = gen.nextInt(100);
r3 = gen.nextInt(100);
r4 = gen.nextInt(100);
tX = x[r1];
x[r1] = x[r2];
x[r2] = tX;
tY = y[r1];
y[r1] = y[r2];
y[r2] = tY;
tX = x[r3];
x[r3] = x[r4];
x[r4] = tX;
tY = y[r3];
y[r3] = y[r4];
y[r4] = tY;
newDistance = 0;
for (int i=1; i < 100; i++)
newDistance += Math.abs(x[i-1]-x[i]) + Math.abs(y[i-1]-y[i]);
if(newDistance < startDistance)
break;
tX = x[r1];
x[r1] = x[r2];
x[r2] = tX;
tY = y[r1];
y[r1] = y[r2];
y[r2] = tY;
tX = x[r3];
x[r3] = x[r4];
x[r4] = tX;
tY = y[r3];
y[r3] = y[r4];
y[r4] = tY;
}
output(x,y);
}
}

• Ehhmm... Shouldn't you put your minions into a thread instead of calling run()? AFAIK this isn't multithreading... – CommonGuy May 20 '14 at 11:59
• @Manu You caught me! I tried to learn it on my own and failed. Any pointers? – Rainbolt May 20 '14 at 13:16
• I think it should be new Thread(new Minion()).start() – CommonGuy May 20 '14 at 13:23
• @Manu Thanks. Apparently I only read half of this tutorial when I was coding. – Rainbolt May 20 '14 at 13:33

# TwoSwapBot

An upgrade to SwapBot, this guy checks for every pair of swaps. First, he checks if any single swap will shorten the path. If it does, it returns immediately. If not, he checks each to see if another swap will shorten it. If not, he just dies.

While the path is still semi-random, it normally returns in about 100ms. If he has to check each and every 2-swap (roughly 25M), it takes about 20 seconds.

At the time of submission, this beat all other competitors in test rounds.

public class TwoSwapBot {

static int numPoints = 100;

String run(String input){
String[] tokens = input.split(",");
if(tokens.length < numPoints*2)
return "bad input? nope. no way. bye.";

Point[] points = new Point[numPoints];
for(int i=0;i<numPoints;i++)
points[i] = new Point(Integer.valueOf(tokens[i*2+1]), Integer.valueOf(tokens[i*2+2]));
int startDist = totalDistance(points);

Point[][] swapped = new Point[(numPoints*(numPoints+1))/2][];
int idx = 0;
for(int i=0;i<numPoints;i++){
for(int j=i+1;j<numPoints;j++){
Point[] test = copyPoints(points);
swapPoints(test,i,j);
int testDist = totalDistance(test);
if( testDist < startDist)
return getPathString(test);
else
swapped[idx++] = test;
}
}

for(int i=0;i<idx;i++){
for(int k=0;k<numPoints;k++){
for(int l=k+1;l<numPoints;l++){
swapPoints(swapped[i],k,l);
if(totalDistance(swapped[i]) < startDist)
return getPathString(swapped[i]);
swapPoints(swapped[i],k,l);
}
}
}
return "well damn";
}

void swapPoints(Point[] in, int a, int b){
Point tmp = in[a];
in[a] = in[b];
in[b] = tmp;
}

String getPathString(Point[] in){
String path = "";
for(int i=0;i<numPoints;i++)
path += in[i].x + "," + in[i].y + ",";
return path.substring(0,path.length()-1);
}

Point[] copyPoints(Point[] in){
Point[] out = new Point[in.length];
for(int i=0;i<out.length;i++)
out[i] = new Point(in[i].x, in[i].y);
return out;
}

static int totalDistance(Point[] in){
int dist = 0;
for(int i=0;i<numPoints-1;i++)
dist += in[i].distance(in[i+1]);
return dist + in[numPoints-1].distance(in[0]);
}

public static void main(String[] args) {
if(args.length < 1)
return;
System.out.print(new TwoSwapBot().run(args[0]));
}

class Point{
final int x; final int y;
Point(int x, int y){this.x = x; this.y = y;}
int distance(Point other){return Math.abs(x-other.x) + Math.abs(y-other.y);}
}
}


This bot

1. Splits the 100 points up in 410 pieces of 2510 points
2. Starts a thread for each piece
3. In the thread, randomly shuffle the array, while keeping the start- and endpoint fixed
4. If distance of new array is shorter, keep it
5. After 59s, the main thread collects the results and prints it out

The idea is to find the best improvement to the path, so that the other bots will fail with their logic.

import java.util.Arrays;
import java.util.Collections;

public static final int THREAD_COUNT = 10;
public static final int DOT_COUNT = 100;
private final Dot[] startDots = new Dot[THREAD_COUNT];
private final Dot[] endDots = new Dot[THREAD_COUNT];
private final Worker worker[] = new Worker[THREAD_COUNT];
private final static long TIME = 59000;

public static void main(String[] args) {
//remove unnecessary player count to make calculation easier
}

public void letWorkersWork(String[] args) {
startWorkers();
waitForWorkers();
printOutput();
}

for (int i = 0; i < args.length; i += 2) {
Dot dot = new Dot(Integer.parseInt(args[i]), Integer.parseInt(args[i + 1]));
if (i % magigNumber == 0) {
startDots[i / magigNumber] = dot;
} else if (i % magigNumber == magigNumber - 2) {
endDots[i / magigNumber] = dot;
} else {
middleDots[i / magigNumber][(i % magigNumber) / 2 - 1] = dot;
}
}
}

private void startWorkers() {
for (int i = 0; i < THREAD_COUNT; i++) {
worker[i] = new Worker(startDots[i], endDots[i], middleDots[i]);
}
}

private void waitForWorkers() {
try {
} catch (InterruptedException e) {
}
}

private void printOutput() {
//get results
Worker.stopWriting = true;
int workerOfTheYear = 0;
int bestDiff = 0;
for (int i = 0; i < THREAD_COUNT; i++) {
if (worker[i].diff() > bestDiff) {
bestDiff = worker[i].diff();
workerOfTheYear = i;
}
}
//build output
StringBuilder result = new StringBuilder(1000);
for (int i = 0; i < THREAD_COUNT; i++) {
result.append(startDots[i]);
Dot middle[] = middleDots[i];
if (i == workerOfTheYear) {
middle = worker[i].bestMiddle;
}
for (int j = 0; j < middle.length; j++) {
result.append(middle[j]);
}
result.append(endDots[i]);
}
result.replace(0, 1, ""); //replace first comma
System.out.print(result);
}

}

class Worker implements Runnable {

public Dot start;
public Dot end;
public static boolean stopWriting = false;
private int bestDist = Integer.MAX_VALUE;
private final int startDist;

public Worker(Dot start, Dot end, Dot[] middle) {
this.start = start;
this.end = end;
System.arraycopy(middle, 0, this.middle, 0, middle.length);
System.arraycopy(middle, 0, this.bestMiddle, 0, middle.length);
startDist = Dot.totalDist(start, middle, end);
}

@Override
public void run() {
while (true) {
shuffleArray(middle);
int newDist = Dot.totalDist(start, middle, end);
if (!stopWriting && newDist < bestDist) {
System.arraycopy(middle, 0, bestMiddle, 0, middle.length);
bestDist = newDist;
}
}
}

public int diff() {
return startDist - bestDist;
}

private void shuffleArray(Dot[] ar) {
Collections.shuffle(Arrays.asList(ar));
}

}

class Dot {

public final int x;
public final int y;

public Dot(int x, int y) {
this.x = x;
this.y = y;
}

public int distTo(Dot other) {
return Math.abs(x - other.x) + Math.abs(y - other.y);
}

public static int totalDist(Dot start, Dot[] dots, Dot end) {
int distance = end.distTo(start);
distance += start.distTo(dots[0]);
distance += end.distTo(dots[dots.length - 1]);
for (int i = 1; i < dots.length; i++) {
distance += dots[i].distTo(dots[i - 1]);
}
return distance;
}

@Override
public String toString() {
return "," + x + "," + y;
}
}

• Note: I changed println to print to get rid of the newline at the end of your output. Otherwise it crashes out. – Geobits May 22 '14 at 1:23
• Changed println to print and made the thread-count dynamic. It now starts with 10 threads... – CommonGuy May 24 '14 at 9:34

# Divide And Conquer + Greedy Bot

NOTE: I looked at your code for Game which includes the following in Game.parsePath:

for(int i=0;i<numPoints;i++){
test[i] = new Point(Integer.valueOf(tokens[i*2]), Integer.valueOf(tokens[i*2+1]));
if(test[i].equals(currentPath[i]))
same++;


However, there is no catch(NumberFormatException) block, so your program will likely crash when a player program outputs a string (as demonstrated at the end of my program's main method). I advise you fix this, because programs could output exceptions, stack traces, or the like. Otherwise, comment out that line in my program before you run it.

## Back to the topic

This implementation (in Java) splits the list of points into chunks of 25, randomly spaced on the list. Then, it creates threads to shorten the path between the points in each chunk (Hence, "Divide and Conquer"). The main thread monitors the others and makes sure to present the shortest solution within the time limit. If a thread dies with or without a solution, it starts another thread off again on a different chunk.

Each thread uses the "greedy" algorithm, which starts off on a random point, goes to the closest point, and repeats until all the points are covered (Hence, "greedy").

### Notes

• This will run through the full 1 minute (I gave 3 seconds for program startup/shutdown and JVM startup - you never know what the JVM startup routines get caught up on next...)
• Even if it has found solutions, it will keep searching and after the 1 minute is up it presents the best solution it found.
• I'm not sure if this implementation is actually any good. I had some fun coding it though :)
• Since a lot of things are random, this may not give the same output for the same input.

Just compile and use java DivideAndConquer.class to run.

public class DivideAndConquer extends Thread {
static Solution best;
static final Object _lock=new Object();

public static void main(String[] args){
if(args.length != 1) {
System.exit(-1);
}
// make sure we don't sleep too long... get the start time
long startTime = System.currentTimeMillis();
// parse input
String[] input=args[0].split(",");
int numPlayers=Integer.parseInt(input[0]);
for(int i=1;i<input.length;i+=2){
}
for(int i=0;i<6;i++)
// sleep
try {
} catch(Exception e){} // should never happen, so ignore
// time to collect the results!
Solution best=getBestSolution();
if(best!=null){
best.apply(original);
String printStr="";
for(int i=0;i<original.size();i++){
Point printPoint=original.get(i);
printStr+=printPoint.x+","+printPoint.y+",";
}
printStr=printStr.substring(0, printStr.length()-1);
System.out.print(printStr);
} else {
System.out.println("Hey, I wonder if the tournament program crashes on NumberFormatExceptions... Anyway, we failed");
}
}

// check the distance
public static int calculateDistance(List<Point> points){
int distance = 0;
for(int i=0;i<points.size();i++){
int next=i+1;
if(next>=points.size())next=0;
distance+=points.get(i).distance(points.get(next));
}
return distance;
}

public static void solutionFound(Solution s){
// thanks to Java for having thread safety features built in
synchronized(_lock){
// thanks to Java again for short-circuit evaluation
// saves lazy programmers lines of code all the time
if(best==null || best.distDifference < s.distDifference){
best=s;
}
}
}

public static Solution getBestSolution(){
// make sure we don't accidentally return
// the best Solution before it's even
// done constructing
synchronized(_lock){
return best;
}
}

List<Point> myPoints;
int start;
int length;
int id;

public DivideAndConquer(int id){
super("DivideAndConquer-Processor-"+(id));
this.id=id;
start=(int) (Math.random()*75);
length=25;
for(int i=start;i<start+length;i++){
}
start();
}

public void run(){
// copy yet again so we can delete from it
int originalDistance=calculateDistance(copied);
// this is our solution list
int randomIdx=new Random().nextInt(copied.size());
Point prev=copied.get(randomIdx);
copied.remove(randomIdx);
while(copied.size()>0){
int idx=-1;
int len = -1;
for(int i=0;i<copied.size();i++){
Point currentPoint=copied.get(i);
int dist=prev.distance(currentPoint);
if(len==-1 || dist<len){
len=dist;
idx=i;
}
}
prev=copied.get(idx);
copied.remove(idx);
}
// aaaand check our distance
int finalDistance=calculateDistance(solution);
if(finalDistance<originalDistance){
// yes! solution
Solution aSolution=new Solution(start, length, solution, originalDistance-finalDistance);
solutionFound(aSolution);
}
// start over regardless
bots.set(id, new DivideAndConquer(id));
}

// represents a solution
static class Solution {
int start;
int length;
int distDifference;
List<Point> region;
public Solution(int start, int length, List<Point> region, int distDifference){
this.start=start;
this.length=length;
this.distDifference=distDifference;
}
public void apply(List<Point> points){
for(int i=0;i<length;i++){
points.set(i+start, region.get(i));
}
}
}

// copied your Point class, sorry but it's useful...
// just added public to each method for aesthetics
static class Point{
int x;
int y;
Point(int x, int y){
this.x = x;
this.y = y;
}
Point(Point other){
this.x = other.x;
this.y = other.y;
}
public boolean equals(Point other){
if(this.x==other.x && this.y==other.y)
return true;
return false;
}

public int distance(Point other){
return Math.abs(x-other.x) + Math.abs(y-other.y);
}
}
}

• Can you believe it? SX asked me for a captcha when I submitted this! Does this look bot-made to you? Seriously? – DankMemes May 14 '14 at 19:27
• Fix for NFException pushed. It will now just kill the player instead of killing the program. – Geobits May 14 '14 at 19:33
• For the record, I don't think it would have crashed on your "Hey, I wonder if..." line anyway. It checks if there are <200 tokens before it tries parsing. Still better to check for it anyway. – Geobits May 14 '14 at 23:56
• @Geobits haha didn't realize that – DankMemes May 15 '14 at 1:35
• Note: To get this to compile, I had to add a ) on line 19; change substr to substring on 38; initialize idx to something in the run() method. – Geobits May 16 '14 at 1:01