# Food shortage in the Snakepit

For the first time in 35 years, the snakepit is running out of food. The inhabitant snakes now have to fight each other in order to survive this food shortage. Only one snake can stand at the top of the food chain!

Not here yet!

Last update on February, 24th

Link to visualizations of last matches

## Description

If you want to fight for the last remaining apples/cherries/whatever, you have to provide a snake in form of a program that accepts a given input and returns its next move.

The only twist is that you're not alone in your pit. Another snake will try to get the rare food too! But it's dark inside the snakepit so you can only see yourself and the apple. Crashing into your opponent will result in your death, just like biting yourself or hitting a wall. Additionally, because apples are rare these days, you starve if your opponent ate enough to reach a length of 7.

The snakepit is a two-dimensional map with a width and height of 15, while the outermost tiles build an impassable wall:

  0 1 2 . . . c d e
0 # # # # # # # # #
1 #               #
2 #           x   #
. #               #
. #               #
. #               #
c #               #
d #               #
e # # # # # # # # #


Coordinates are zero-indexed, so the point where the x is would be 12,2.

Your bot will be called with two arguments:

• The location of the food
• The locations of your body segments, separated by /

It should then write one of the following to stdout:

• L for a quarter left turn as its next move
• R for a quarter right turn
• Anything else for a move in the same direction

Example:

Projects/Snakepit> python bot.py 12,2 4,8/4,9/3,9/2,9
'R'
Projects/Snakepit>


## Rules

• Output anything, because anything is a valid move
• Read/write files in its own directory which is located under ./snakes/ThisIsYourSnake
• Run on Ubuntu 14.04 and Windows 7 (it actually has to)

• Read/write files outside its own directory
• Use external resources such as the internet
• Have a runtime above 10 seconds per execution

• The source code of the bot
• A bot/snake name
• A command to run your bot

If you want to make my life easier, please provide a line like CoolSnake MyOwnName python bot.py.

## Scoring

Your snake gets a point for winning a game against another snake. A game is won under the following circumstances:

• Your opponent hits himself, you or a wall
• You reach length 7

Additionally, both snakes starve after 200 cycles.

Each snake will fight 10 matches for their survival against each other snake.

## Example Bots

Just to give you an idea, I'll provide these two (participating) example snakes:

SneakySnake

#!/usr/bin/env python

import sys, random

def main(food, me) :
food = [int(i) for i in food.split(",")]
me = [[int(i) for i in seg.split(",")] for seg in me.split("/")]

vn = [-1, 0]
vn = [1, 0]
vn = [0, -1]
vn = [0, 1]

if v == vn :
return "..."
elif [-v[1], v[0]] == vn :
return "R"
elif [v[1], -v[0]] == vn :
return "L"
else :
return random.choice(("R", "L"))

if __name__ == "__main__" :
print main(*sys.argv[1:3])


SneakySnake Cipher python bot.py

ViciousViper

#!/usr/bin/env python

import sys, random

def main(food, me) :
food = [int(i) for i in food.split(",")]
me = [[int(i) for i in seg.split(",")] for seg in me.split("/")]
if 0 not in vn :
vn[v.index(0)-1] = 0
vn[vn.index(0)-1] = vn[vn.index(0)-1] / abs(vn[vn.index(0)-1])

if v == vn :
return "..."
elif [v[0] + vn[0], v[1] + vn[1]] == [0, 0] :
return random.choice(("R", "L"))
else :
return "R" if [-v[1], v[0]] == vn else "L"

if __name__ == "__main__" :
print main(*sys.argv[1:3])


ViciousViper Cipher python bot.py

And their matches:

## Control program

You can find the control program on github, along with all bots and records of past matches.

Requirements:

• Python 2 + the libraries numpy and pillow (you can check if they are present via python -c "import numpy, PIL", if it throws errors the modules are missing)
• Copying the full folder structure is required in order for the controller to work
• Register your bot(s) in the ./snakes/list.txt file in the style of CoolSnake MyOwnName Command To Run My Bot
• Place your bot inside a directory with its name under ./snakes
• Neither your, nor your bot's name is allowed to contain whitespaces!

Usage:

python run.py [-h] [-n int] [-s int] [-l int] [-c int] [-g]

python run.py will run the tournament with all bots registered in list.txt and the standard properties. Advanced options are:

• -h displays a help message
• -n int rounds of battles for each combination of opponents
• -s int determines the size of the grid (width and height)
• -l int sets the required length to win
• -c int sets the limit of cycles
• -g or --no-gifs creates no gifs of the matches
• I'm not sure how much interesting interacting strategies you'll get if bots have almost no way of knowing where any other bots are. – Martin Ender Feb 24 '15 at 13:49
• Since we're limited to length <7, even knowing where the opponent is only matters for avoidance. You're not really long enough to do anything but the simplest of blocking. I suspect that most matches will just come down to who was closest to each food as it spawned. – Geobits Feb 24 '15 at 13:59
• I had an amazing plan for how to do really really well in this. Then I read the rules more. If you can't see the other snake your options are really "take the direct path" or "take some longer path". Neither are very interesting if it is just dumb luck avoiding the other snake. – captncraig Feb 24 '15 at 16:17
• I don't think you guys have thought this through. When your enemy takes an apple, you know exactly where he is. You can then predict the path he will take to the current apple. You may figure out you can reach it first with no chance of collision, or you may lay a trap by dragging your tail across his likely path. You can also estimate his strategy by how long it takes him to get an apple, or when he has collided with your tail in previous games. And, of course, he knows that you know that he knows...etc. Excellent puzzle Cipher +1. – Logic Knight Feb 25 '15 at 2:54
• I'm thinking about creating a separated tournament with more focus on the original game and stuff like seeing your opponent, increased length limit, etc. Anyone interested? – Cipher Feb 25 '15 at 8:01

# Zen - C++

This Codémon is not here to eat but to fight. He knows that a dead enemy will not stole his apples.

Name| Author |Launch with

## Strategy

Everybody (excepted CircleOfLife) rush to the apples, but not Zen, not always. If the enemy can reach the food before him, he simply wait at the center (what? but what are you doing here, CircleOfLife?). Else, Zen goes at the apple and turns around while waiting that something happens. In fact, he uses the apple as a bait.

I didn't coded anything against the curious strategy of CircleOfLife because he can only win with very very much luck.

## The code

This is the complete code of the C++ project. Cut the 11 source files and the Makefile and compile with make

$cat src/* Makefile /* * @file Enemy.cpp * @author GholGoth21 * @date Créé le 1 mars 2015 à 14:10 */ #include "Enemy.h" #include <fstream> Enemy::Enemy() { } Enemy::~Enemy() { } std::ostream &operator<<(std::ostream &os, const Enemy& e) { return os<<e.m_pos<<" "<<e.m_date; } std::istream &operator>>(std::istream &is, Enemy& e) { return is>>e.m_pos>>e.m_date; } int Enemy::distTo(int2 const &target, int date) const { return m_pos.distTo(target)-(date-m_date); } bool Enemy::recentActivity(int2 const &pos, int date, int maxDelay) const { return pos.distTo(m_pos)<=date-m_date && date-m_date<=maxDelay; } /* * @file Enemy.h * @author GholGoth21 * @date Créé le 1 mars 2015 à 14:10 */ #ifndef ENEMY_H #define ENEMY_H #include "int2.h" class Enemy { public: Enemy(); virtual ~Enemy(); public: void setPos(int2 const &pos, int date) { m_pos=pos; m_date=date; } int distTo(int2 const &target, int date) const; int2 const &pos() const { return m_pos; } bool recentActivity(int2 const &pos, int date, int maxDelay) const; friend std::ostream &operator<<(std::ostream &os, const Enemy& e); friend std::istream &operator>>(std::istream &is, Enemy& e); private: int2 m_pos; int m_date; }; #endif /* ENEMY_H */ /* * @file Snake.cpp * @author GholGoth21 * @date Créé le 28 février 2015 à 17:47 */ #include "Snake.h" #include "enums.h" #include "StrManip.h" #include "Enemy.h" #include <vector> #include <cmath> Snake::Snake(std::string const &body) { std::vector<std::string> posList; split(body, '/', posList); for(auto &pos : posList) m_body.push_back(int2(pos)); } Snake::~Snake() { } Command Snake::move(int2 food, int date, Enemy const &enemy) { Command bestCommand[Command::count]; int myDist=curPos().distTo(food); int enemyDist=enemy.distTo(food,date); if(myDist>=enemyDist && enemyDist>2) { orderCommand(int2(MAPSIZE/2,MAPSIZE/2), bestCommand); for(int i=0; i<Command::count; i++) if(validCommand(bestCommand[i]) && !enemy.recentActivity(nextPos(bestCommand[i]),date,5)) return bestCommand[i]; } if((myDist==1 && enemyDist>((len()-1)/2)*2+3) || enemyDist<-5) { orderCommand(food, bestCommand); for(int i=0; i<Command::count; i++) if(validCommand(bestCommand[i])) return bestCommand[i]; } int2 embushPoint; int minDist=-1; foreach_enum(Direction, d) { int2 point(food+d.vector()); int dist=point.quadDistTo(enemy.pos()); if(dist<minDist || minDist<0) { minDist=dist; embushPoint=point; } } if(curPos().distTo(embushPoint)<enemy.distTo(embushPoint,date)-((len()-1)/2)*2) { int minimalAction=-1; int qMinDist = curPos().quadDistTo(embushPoint); Command minimalCommand; foreach_enum(Command, c) { int2 np=nextPos(c); int qDist = np.quadDistTo(embushPoint); if((qDist<minimalAction || minimalAction<0) && qDist>qMinDist && validCommand(c)) { minimalAction=qDist; minimalCommand=c; } } return minimalCommand; } else { orderCommand(embushPoint, food, bestCommand); for(int i=0; i<Command::count; i++) if(validCommand(bestCommand[i]) && nextPos(bestCommand[i])!=food) return bestCommand[i]; } return Command::forward; } bool Snake::validCommand(Command c) const { if(!c.isValid()) return false; int2 np = nextPos(c); if(!(0<np.x && np.x<MAPSIZE-1 && 0<np.y && np.y<MAPSIZE-1)) return false; for(unsigned int i=2; i<m_body.size()-1; i++) if(np==m_body.at(i)) return false; return true; } bool Snake::isStarting() const { if(m_body.size()==3) { if(m_body.at(0)==int2(3,(MAPSIZE)/2) && m_body.at(1)==int2(2,(MAPSIZE)/2) && m_body.at(2)==int2(1,(MAPSIZE)/2)) return true; else if(m_body.at(0)==int2(MAPSIZE-4,(MAPSIZE)/2) && m_body.at(1)==int2(MAPSIZE-3,(MAPSIZE)/2) && m_body.at(2)==int2(MAPSIZE-2,(MAPSIZE)/2)) return true; } return false; } void Snake::orderCommand(int2 target, Command *tab) { int weight[Command::count]; foreach_enum(Command, c) { int2 np = nextPos(c); weight[c]=np.quadDistTo(target); tab[c]=c; } for(int i=0; i<Command::count-1; i++) { while(i>=0 && weight[tab[i]]>weight[tab[i+1]]) { varSwitch(tab[i], tab[i+1]); i--; } } } void Snake::orderCommand(int2 target1, int2 target2, Command *tab) { int weight[Command::count]; foreach_enum(Command, c) { int2 np = nextPos(c); weight[c]=np.quadDistTo(target1)+np.quadDistTo(target2); tab[c]=c; } for(int i=0; i<Command::count-1; i++) { while(i>=0 && weight[tab[i]]>weight[tab[i+1]]) { varSwitch(tab[i], tab[i+1]); i--; } } } /* * @file Snake.h * @author GholGoth21 * @date Créé le 28 février 2015 à 17:47 */ #ifndef SNAKE_H #define SNAKE_H #include "int2.h" #include <vector> #define MAPSIZE 15 class Enemy; class Snake { public: Snake(std::string const &body); virtual ~Snake(); public: Command move(int2 food, int date, Enemy const &enemy); Direction curDir() const { return (m_body.at(0)-m_body.at(1)).direction(); } int2 curPos() const { return m_body.at(0); } int2 nextPos(Command c) const { return curPos()+curDir().applyCommand(c).vector(); } bool validCommand(Command c) const; bool isStarting() const; void orderCommand(int2 target, Command *tab); void orderCommand(int2 target1, int2 target2, Command *tab); int len() const { return m_body.size(); } private: std::vector<int2> m_body; }; #endif /* SNAKE_H */ /* * @file StrManip.cpp * @author GholGoth21 * @date Créé le 7 février 2015 à 17:26 */ #include "StrManip.h" #include <sstream> std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) { std::stringstream ss(s); std::string item; while(std::getline(ss, item, delim)) elems.push_back(item); return elems; } int atoi(std::string const &text) { std::stringstream ss(text); int val; ss >> val; return val; } /* * @file StrManip.h * @author GholGoth21 * @date Créé le 7 février 2015 à 17:26 */ #ifndef STRMANIP_H #define STRMANIP_H #include <string> #include <vector> std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems); int atoi(std::string const &text); #endif /* STRMANIP_H */ /* * @file enums.cpp * @author GholGoth21 * @date Créé le 28 février 2015 à 17:55 */ #include "enums.h" #include "int2.h" Command Direction::turnTo(Direction newDir) const { if(!isValid() || !newDir.isValid()) return Command::count; //Invalid else if((m_value==Direction::up && newDir==Direction::left) || (m_value==Direction::left && newDir==Direction::down) || (m_value==Direction::down && newDir==Direction::right) || (m_value==Direction::right && newDir==Direction::up)) return Command::left; else if((m_value==Direction::up && newDir==Direction::right) || (m_value==Direction::right && newDir==Direction::down) || (m_value==Direction::down && newDir==Direction::left) || (m_value==Direction::left && newDir==Direction::up)) return Command::right; else if(m_value==newDir) return Command::forward; else return Command::count; // Invalid } Direction Direction::applyCommand(Command c) const { if(c==Command::forward) return m_value; else if(c==Command::left) { switch(m_value) { case Direction::left: return Direction::down; case Direction::up: return Direction::left; case Direction::right: return Direction::up; case Direction::down: return Direction::right; default: break; } } else if(c==Command::right) { switch(m_value) { case Direction::left: return Direction::up; case Direction::up: return Direction::right; case Direction::right: return Direction::down; case Direction::down: return Direction::left; default: break; } } return Direction::count; // Invalid } int2 Direction::vector() const { switch(m_value) { case Direction::left: return int2(-1,0); case Direction::up: return int2(0,-1); case Direction::right: return int2(1,0); case Direction::down: return int2(0,1); default: return int2(0,0); } } std::ostream &operator<<(std::ostream &os, const Command& c) { switch(c.m_value) { case Command::left: return os<<"L"; case Command::right: return os<<"R"; default: return os<<"F"; } } /* * @file enums.h * @author GholGoth21 * @date Créé le 28 février 2015 à 17:55 */ #ifndef ENUMS_H #define ENUMS_H #include <ostream> struct int2; #define DECL_ENUM_STRUCT(_name) \ _name() : m_value(static_cast<Type>(0)) {} \ _name(Type value) : m_value(value) {} \ _name(int value) : m_value(static_cast<Type>(value)) {} \ static Type begin() { return static_cast<Type>(0); } \ static Type end() { return count; } \ _name &operator++() { m_value=static_cast<Type>(static_cast<int>(m_value)+1); return *this; } \ operator int() const { return static_cast<Type>(m_value); } \ Type m_value; #define foreach_enum(_type,_var) for(_type _var = _type::begin(); _var<_type::end(); ++_var) struct Command { enum Type { left, forward, right, count }; bool isValid() const { return m_value<count; } friend std::ostream &operator<<(std::ostream &os, const Command& c); DECL_ENUM_STRUCT(Command) }; struct Direction { enum Type { left, up, right, down, count }; bool isValid() const { return m_value<count; } Command turnTo(Direction newDir) const; Direction applyCommand(Command c) const; int2 vector() const; DECL_ENUM_STRUCT(Direction) }; #endif /* ENUMS_H */ /* * @file int2.cpp * @author GholGoth21 * @date Créé le 28 février 2015 à 17:37 */ #include "int2.h" #include "enums.h" #include "StrManip.h" #include <vector> #include <cmath> int2::int2() { } int2::~int2() { } int2::int2(std::string const &text) { std::vector<std::string> posList; split(text, ',', posList); x=atoi(posList.at(0)); y=atoi(posList.at(1)); } Direction int2::direction() const { if(x==0 && y==0) return Direction::count; // Invalid else if(y>=std::abs(x)) return Direction::down; else if(x>=std::abs(y)) return Direction::right; else if(x<=-std::abs(y)) return Direction::left; else return Direction::up; } Direction int2::secondary() const { if(x==0 || y==0) return Direction::count; //Invalid else if(y<=std::abs(x) && y>=0) return Direction::down; else if(x<=std::abs(y) && x>=0) return Direction::right; else if(x>=-std::abs(y) && x<=0) return Direction::left; else return Direction::up; } int int2::distTo(int2 const &other) const { return std::abs(x-other.x)+std::abs(y-other.y); } int int2::quadDistTo(int2 const &other) const { return sq(x-other.x)+sq(y-other.y); } int2 int2::operator+(int2 const &other) const { return int2(x+other.x,y+other.y); } int2 int2::operator-(int2 const &other) const { return int2(x-other.x,y-other.y); } std::ostream &operator<<(std::ostream &os, const int2& c) { return os<<c.x<<","<<c.y; } std::istream &operator>>(std::istream &is, int2& c) { std::string text; is>>text; c=int2(text); return is; } /* * @file int2.h * @author GholGoth21 * @date Créé le 28 février 2015 à 17:37 */ #ifndef INT2_H #define INT2_H #include "enums.h" #include <string> struct int2 { public: int2(); int2(int p_x, int p_y) : x(p_x), y(p_y) {} int2(std::string const &text); virtual ~int2(); public: Direction direction() const; Direction secondary() const; int distTo(int2 const &other) const; int quadDistTo(int2 const &other) const; int2 operator+(int2 const &other) const; int2 operator-(int2 const &other) const; bool operator==(int2 const &other) const { return x==other.x && y==other.y; } bool operator!=(int2 const &other) const { return x!=other.x || y!=other.y; } friend std::ostream &operator<<(std::ostream &os, const int2& c); friend std::istream &operator>>(std::istream &is, int2& c); public: int x; int y; }; inline int sq(int val) { return val*val; } template<typename T> inline void varSwitch(T &a, T &b) { T tmp=a; a=b; b=tmp; } #endif /* INT2_H */ /* * @file main.cpp * @author GholGoth21 * @date Créé le 28 février 2015 à 17:23 */ #include "int2.h" #include "Snake.h" #include "Enemy.h" #include <cstdlib> #include <iostream> #include <fstream> using namespace std; /* * @brief La fonction principale du programme. * @param argc Le nombre de paramètres passés en ligne de commandes. * @param argv La liste des paramètres passés en ligne de commandes. */ int main(int argc, char** argv) { /* Error handling */ if(argc<3) { cerr<<"Error : not enough arguments on the command line."<<endl; cout<<"F"<<endl; return 1; } /* Init and load */ int2 prevFood; int date = 0; Enemy enemy; ifstream load("PreviousState.txt"); if(load) { load>>date; load>>prevFood; load>>enemy; load.close(); } int2 food(argv[1]); Snake me(argv[2]); if(me.isStarting()) { date=0; if(me.curPos().x<MAPSIZE/2) enemy.setPos(int2(MAPSIZE-4,MAPSIZE/2), 0); else enemy.setPos(int2(3,MAPSIZE/2), 0); } else if(prevFood!=food && me.curPos()!=prevFood) { enemy.setPos(prevFood, date); } /* Moving */ cout<<me.move(food,date,enemy)<<endl; /* Saving */ ofstream save("PreviousState.txt"); if(save) { save<<++date<<endl; save<<food<<endl; save<<enemy<<endl; save.close(); } return 0; } # Makefile HEADERS =$(wildcard $(SRCPATH)/*.h) SOURCES =$(wildcard $(SRCPATH)/*.cpp) OBJECTS =$(patsubst $(SRCPATH)/%.cpp,$(BUILDPATH)/%.o,$(SOURCES)) M = Makefile CFLAGS = -Wall -std=c++11 BINPATH = bin BUILDPATH = build SRCPATH = src ifeq ($(OS),Windows_NT)
EXE = Zen.exe
else
EXE = Zen
endif

$(BINPATH)/$(EXE): $(BINPATH)$(BUILDPATH) $(OBJECTS) g++ -o$@ $(OBJECTS)$(BUILDPATH)/%.o: $(SRCPATH)/%.cpp$(HEADERS) $M g++$(CFLAGS) -o $@ -c$<

$(BINPATH)$(BUILDPATH):
mkdir $@ clean: rm$(OBJECTS)


## Results

|     Name     |   Master   | Score |
|--------------|------------|-------|
| Zen          | GholGoth21 | 24    |
| SneakySnake  | Cipher     | 10    |
| ViciousViper | Cipher     | 6     |
| CircleOfLife | Manu       | 4     |


And some typical battles (ViciousViper vs Zen and SneakySnake vs Zen) :

Edit : I add this very intersting battle against CircleOfLife :

• Nice strategy. Could you upload your project somewhere as a zip so others could use it more easily? – randomra Mar 1 '15 at 20:18

## CircleOfLife (Java)

CircleOfLife Manu java CircleOfLife (Compile with javac CircleOfLife.java)

Runs towards the middle and stays there. I hope that some submissions will crash into it on their way to the food.

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class CircleOfLife {
private static final int UP = 0;
private static final int DOWN = 1;
private static final int LEFT = 2;
private static final int RIGHT = 3;
private static final String GO_RIGHT = "R";
private static final String GO_LEFT = "L";
private static final String GO_FORWARD = "F";
private static int currentDirection = UP;
private static List<Point> snakeParts = new ArrayList<>();

public static void main(String[] args) {
String[] parts = args[1].split("/");
for (String part : parts) {
String[] pos = part.split(",");
int x = Integer.parseInt(pos[0]);
int y = Integer.parseInt(pos[1]);
}
Point neck = snakeParts.get(1);
if (head.y - neck.y == 1) {
currentDirection = DOWN;
} else if (head.x - neck.x == -1) {
currentDirection = LEFT;
} else if (head.x - neck.x == 1) {
currentDirection = RIGHT;
}
makeCircle();
} else {
runToMiddle();
}
}

private static void makeCircle() {
if (!isInMiddle(snakeParts.get(1))) {
System.out.println(GO_FORWARD);
return;
}
Point neck = snakeParts.get(1);
String output = GO_FORWARD;
if (head.x == 8 && neck.x == 8) {
output = currentDirection == UP ? GO_LEFT : GO_RIGHT;
} else if (head.x == 7 && neck.x == 7) {
output = currentDirection == UP ? GO_RIGHT : GO_LEFT;
} else if (head.y == 8 && neck.y == 8) {
output = currentDirection == RIGHT ? GO_LEFT : GO_RIGHT;
} else if (head.y == 7 && neck.y == 7) {
output = currentDirection == RIGHT ? GO_RIGHT : GO_LEFT;
}
System.out.println(output);
}

private static void runToMiddle() {
int dX = 8 - head.x;
int dY = 8 - head.y;
String output = GO_FORWARD;

if (Math.abs(dX) > Math.abs(dY)) {
switch (currentDirection) {
case DOWN: output = dX < 0 ? GO_RIGHT : GO_LEFT; break;
case UP: output = dX < 0 ? GO_LEFT : GO_RIGHT; break;
case RIGHT: output = dX < 0 ? GO_RIGHT : GO_FORWARD; break;
case LEFT: output = dX < 0 ? GO_FORWARD : GO_RIGHT; break;
}
} else {
switch (currentDirection) {
case DOWN: output = dY < 0 ? GO_RIGHT : GO_FORWARD; break;
case UP: output = dY < 0 ? GO_FORWARD : GO_RIGHT; break;
case RIGHT: output = dY < 0 ? GO_LEFT : GO_RIGHT; break;
case LEFT: output = dY < 0 ? GO_RIGHT : GO_LEFT; break;
}
}
System.out.println(output);
}

public static boolean isInMiddle(Point snakePart) {
if ((snakePart.x == 7 || snakePart.x == 8) &&
(snakePart.y == 7 || snakePart.y == 8)) {
return true;
}
return false;
}
}