You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SnakeBot/index.js

468 lines
25 KiB

2 years ago
/*
Welcome to your Code your snake bot!
This is a minimal example showing spawning, the snake's position, and where the closest apple is.
It won't work right away because you need to authenticate yourself. Read /learn/welcome.md.
*/
const LEFT = 0
const RIGHT = 1
const UP = 2
const DOWN = 3
const FOOD = 3
const GOLDEN_FOOD = 4
const BLACK_HOLE = 5
const newSnake = require('./wrapper.js')
const dotenv = require('dotenv');
dotenv.config();
2 years ago
const fs = require('fs');
2 years ago
let snake = newSnake(process.env.auth) // Login with the auth cookie in the secrets tab
var vals = fs.readFileSync("data", 'utf8').split("\n")
/*File Format:
`${LAST_DEATH}
${BEST_LENGTH}
${TOTAL_ATTEMPTS}
${TOTAL_LENGTH}
${BEST_SEEN}
${BEST_SEEN_WHO}
`
*/
2 years ago
horz_dir = LEFT
vert_dir = UP
LAST_DEATH = vals[0]
BEST_LENGTH = Number(vals[1])
TOTAL_ATTEMPTS = Number(vals[2])
TOTAL_LENGTH = Number(vals[3])
2 years ago
LAST_LENGTH = 0
BEST_SEEN = Number(vals[4])
BEST_SEEN_WHO = vals[5]
2 years ago
TEST_REGIONS = []
APPLES_LIST=[]
targetApp={}
2 years ago
snake.onSpawn = function(x, y) { // When the snake spawns
console.log(`Spawned at X ${x}, Y ${y}`)
TOTAL_ATTEMPTS++
}
snake.onDeath = function(reason) { // When the snake dies
LAST_DEATH = reason
TOTAL_LENGTH += LAST_LENGTH
LAST_LENGTH = 0
fs.writeFileSync("data",
`${LAST_DEATH}
${BEST_LENGTH}
${TOTAL_ATTEMPTS}
${TOTAL_LENGTH}
${BEST_SEEN}
${BEST_SEEN_WHO}
`)
2 years ago
snake.spawn() // Respawn
}
snake.onTick = function(obj) { // When the game updates
if (snake.spawned) { // If the snake is alive
let closestApple = snake.closestApple() // Get the closest apple
let bestApp = bestApple()
if (snake.me()) {
if (snake.me()?.body.length > BEST_LENGTH) {
BEST_LENGTH = snake.me().body.length
}
LAST_LENGTH = snake.me().body.length
for (var player in obj.players) {
if (obj.players[player].body.length > BEST_SEEN) {
BEST_SEEN = obj.players[player].body.length
BEST_SEEN_WHO = player
}
}
}
// Clear the console and log the snake's position and the closest apple's position
console.clear()
console.log(`I am at X ${snake.x}, Y ${snake.y}`)
console.log(`The closest apple is at X ${closestApple.x}, Y ${closestApple.y}`)
console.log(`The best apple is at X ${bestApp.x}, Y ${bestApp.y}`)
console.log(`Last Death reason was: ${LAST_DEATH}`)
console.log(`\nBest Length:${BEST_LENGTH}\nAverage Length:${Math.floor((TOTAL_LENGTH + snake.me()?.body.length) / TOTAL_ATTEMPTS)}\nCurrent Length:${snake.me()?.body.length}\nTotal Spawns:${TOTAL_ATTEMPTS}\n\nBest Seen:${BEST_SEEN_WHO} - ${BEST_SEEN}`)
2 years ago
//console.log(JSON.stringify(obj));
function bestApple() {
//Returns the best apple that will not result in greedy collisions.
var testApple = snake.closestApple()
var allowed = true;
var adjacentBodies=0;
for (var player in obj.players) {
if (player !== "sigonasr2") {
for (var i of obj.players[player].body) {
adjacentBodies+=(Math.abs(i[0] - testApple.x)===0)&&(Math.abs(i[1] - testApple.y)===1)||(Math.abs(i[0] - testApple.x)===1)&&(Math.abs(i[1] - testApple.y)===0)?1:0
if (Math.abs(i[0] - testApple.x) < 3 && Math.abs(i[1] - testApple.y) < 3) {
allowed = false;
break;
}
if (Math.abs(obj.players[player].head[0] - testApple.x) < 2 && Math.abs(obj.players[player].head[1] - testApple.y) < 2) {
allowed = false;
break;
}
}
} else {
adjacentBodies+=(Math.abs(obj.players[player].body[0] - testApple.x)===0)&&(1,Math.abs(obj.players[player].body[1] - testApple.y)===1)||(Math.abs(obj.players[player].body[0] - testApple.x)===1)&&(1,Math.abs(obj.players[player].body[1] - testApple.y)===0)?1:0
}
}
for (var i of snake.blackHoles()) {
adjacentBodies+=(Math.abs(i[0] - testApple.x)===0)&&(Math.abs(i[1] - testApple.y)===1)||(Math.abs(i[0] - testApple.x)===1)&&(Math.abs(i[1] - testApple.y)===0)?1:0
}
adjacentBodies+=((testApple.x===0)?1:0)+((testApple.y===0)?1:0)+((testApple.x===149)?1:0)+((testApple.y===149)?1:0) //Boundary checks count as walls too.
if (adjacentBodies>=2) {
allowed=false //We can't allow picking up an apple with only one way out.
}
if (allowed) {
return testApple;
}
for (var apple of snake.apples()) {
var testApple = apple
var allowed = true;
var adjacentBodies=0;
for (var player in obj.players) {
if (player !== "sigonasr2") {
for (var i of obj.players[player].body) {
adjacentBodies+=((Math.abs(i[0] - testApple[0])===0)&&(Math.abs(i[1] - testApple[1])===1)||(Math.abs(i[0] - testApple[0])===1)&&(Math.abs(i[1] - testApple[1])===0))?1:0
if (Math.abs(i[0] - testApple[0]) < 3 && Math.abs(i[1] - testApple[1]) < 3) {
allowed = false;
break;
}
}
if (Math.abs(obj.players[player].head[0] - testApple[0]) < 2 && Math.abs(obj.players[player].head[1] - testApple[1]) < 2) {
allowed = false
break;
}
} else {
adjacentBodies+=(Math.abs(obj.players[player].body[0] - testApple[0])===0)&&(Math.abs(obj.players[player].body[1] - testApple[1])===1)||(Math.abs(obj.players[player].body[0] - testApple[0])===1)&&(Math.abs(obj.players[player].body[1] - testApple[1])===0)?1:0
}
}
for (var i of snake.blackHoles()) {
adjacentBodies+=(Math.abs(i[0] - testApple[0])===0)&&(Math.abs(i[1] - testApple[1])===1)||(Math.abs(i[0] - testApple[0])===1)&&(Math.abs(i[1] - testApple[1])===0)?1:0
}
adjacentBodies+=((testApple[0]===0)?1:0)+((testApple[1]===0)?1:0)+((testApple[0]===149)?1:0)+((testApple[1]===149)?1:0) //Boundary checks count as walls too.
if (adjacentBodies>=2) {
console.log(adjacentBodies+" found for "+testApple)
allowed=false //We can't allow picking up an apple with only one way out.
}
if (allowed) {
return { x: testApple[0], y: testApple[1] };
}
}
console.log("No viable apple found...")
return { x: 0, y: 0 }
}
function spotIsFree(dir, x, y) {
newCoords = [x, y]
for (var i of TEST_REGIONS) {
if (i[0] === newCoords[0] && i[1] === newCoords[1]) {
return false;
}
}
for (var i of snake.me()?.body) {
2 years ago
if (i[0] === newCoords[0] && i[1] === newCoords[1]) {
return false;
}
}
for (var i of snake.blackHoles()) {
if (i[0] === newCoords[0] && i[1] === newCoords[1]) {
return false;
}
}
for (var i of snake.bodyPositions()) {
if (i[0] === newCoords[0] && i[1] === newCoords[1]) {
return false;
}
}
for (var i of snake.headPositions()) {
if (i[0] === newCoords[0] && i[1] === newCoords[1]) {
return false;
}
}
for (var player in obj.players) {
if (player !== "sigonasr2") {
var head = obj.players[player].head
var dir = obj.players[player].direction
if (head[0]+(dir===LEFT?1:dir===RIGHT?-1:0)===newCoords[0]&&head[1]+(dir===DOWN?1:dir===UP?-1:0)===newCoords[1]) {
return false
}
}
}
2 years ago
if (newCoords[0] < 0 || newCoords[0] > 149 || newCoords[1] < 0 || newCoords[1] > 149) {
return false;
}
return true;
}
function findPath(dir, x, y,appleDepth=11) {
2 years ago
if (spotIsFree(dir, x, y)) {
TEST_REGIONS = [[x, y], ...TEST_REGIONS]
diff_x = x - targetApp.x
diff_y = y - targetApp.y
2 years ago
if (diff_x === 0 && diff_y === 0) {
if (--appleDepth===0) {
return true;
} else {
var myRegions = [...TEST_REGIONS]
while (APPLES_LIST.length>0) {
var newApple = APPLES_LIST.shift()
TEST_REGIONS=[...myRegions]
targetApp={x:newApple[0],y:newApple[1]}
diff_x = x - targetApp.x
diff_y = y - targetApp.y
if (Math.abs(diff_x) > Math.abs(diff_y)) {
if (diff_x > 0) {
if (dir !== RIGHT && findPath(LEFT, x - 1, y,appleDepth)) {
return true;
} //Preferred direction found.
} else {
if (dir !== LEFT && findPath(RIGHT, x + 1, y,appleDepth)) {
return true;
}
}
} else {
if (diff_y > 0) {
if (dir !== DOWN && findPath(UP, x, y - 1,appleDepth)) {
return true;
} //Preferred direction found.
} else {
if (dir !== UP && findPath(DOWN, x, y + 1,appleDepth)) {
return true;
}
}
}
}
}
2 years ago
}
if (Math.abs(diff_x) > Math.abs(diff_y)) {
if (diff_x > 0) {
if (dir !== RIGHT && findPath(LEFT, x - 1, y,appleDepth)) {
2 years ago
return true;
} //Preferred direction found.
} else {
if (dir !== LEFT && findPath(RIGHT, x + 1, y,appleDepth)) {
2 years ago
return true;
}
}
} else {
if (diff_y > 0) {
if (dir !== DOWN && findPath(UP, x, y - 1,appleDepth)) {
2 years ago
return true;
} //Preferred direction found.
} else {
if (dir !== UP && findPath(DOWN, x, y + 1,appleDepth)) {
2 years ago
return true;
}
}
}
}
return false;
}
function findSafePath(dir, x, y, targetX, targetY,emergency=false) {
if (spotIsFree(dir, x, y)) {
TEST_REGIONS = [[x, y], ...TEST_REGIONS]
diff_x = x - targetX
diff_y = y - targetY
if (diff_x === 0 && diff_y === 0) {
return true;
}
if (Math.abs(diff_x) > Math.abs(diff_y)) {
if (diff_x > 0) {
if (dir !== RIGHT && findSafePath(LEFT, x - 1, y, targetX, targetY)) {
return true;
} //Preferred direction found.
} else {
if (dir !== LEFT && findSafePath(RIGHT, x + 1, y, targetX, targetY)) {
return true;
}
}
} else {
if (diff_y > 0) {
if (dir !== DOWN && findSafePath(UP, x, y - 1, targetX, targetY)) {
return true;
} //Preferred direction found.
} else {
if (dir !== UP && findSafePath(DOWN, x, y + 1, targetX, targetY)) {
return true;
}
}
}
}
return false;
}
//Make a path towards the closest apple. Always play it safe.
diff_x = snake.x - bestApp.x
diff_y = snake.y - bestApp.y
TEST_REGIONS = []
EXTRA_MOVEMENT=false
APPLES_LIST=
snake.apples()
.filter((a)=>a[0]!==bestApp.x&&a[1]!==bestApp.y)
.sort((a,b)=>Math.sqrt(Math.pow(snake.x-a[0],2)+Math.pow(snake.y-a[1],2))-Math.sqrt(Math.pow(snake.x-b[0],2)+Math.pow(snake.y-b[1],2)))
targetApp=bestApp
2 years ago
if (Math.abs(diff_x) > Math.abs(diff_y)) {
if (diff_x > 0) {
if (snake.me()?.direction !== RIGHT && findPath(LEFT, snake.x - 1, snake.y)) {
snake.setDirection(LEFT);
return;
} //Preferred direction found.
} else {
if (snake.me()?.direction !== LEFT && findPath(RIGHT, snake.x + 1, snake.y)) {
snake.setDirection(RIGHT);
return;
}
}
} else {
if (diff_y > 0) {
if (snake.me()?.direction !== DOWN && findPath(UP, snake.x, snake.y - 1)) {
snake.setDirection(UP);
return;
} //Preferred direction found.
} else {
if (snake.me()?.direction !== UP && findPath(DOWN, snake.x, snake.y + 1)) {
snake.setDirection(DOWN);
return;
}
}
}
//TODO Try different apples here before trying to "just survive". There may be a better path.
console.log("Okay maybe this apple wasn't such a good idea... Try another one.")
APPLES_LIST=
snake.apples()
.filter((a)=>a[0]!==bestApp.x&&a[1]!==bestApp.y) //I want apples furthest away to be checked first. These are probably the safest calculations to make.
.sort((a,b)=>Math.sqrt(Math.pow(snake.x-b[0],2)+Math.pow(snake.y-b[1],2))-Math.sqrt(Math.pow(snake.x-a[0],2)+Math.pow(snake.y-a[1],2)))
for (var apple of APPLES_LIST) {
2 years ago
TEST_REGIONS = []
EXTRA_MOVEMENT=false
var adjacentBodies=0;
for (var player in obj.players) {
if (player !== "sigonasr2") {
for (var i of obj.players[player].body) {
adjacentBodies+=((Math.abs(i[0] - apple[0])===0)&&(Math.abs(i[1] - apple[1])===1)||(Math.abs(i[0] - apple[0])===1)&&(Math.abs(i[1] - apple[1])===0))?1:0
}
} else {
adjacentBodies+=((Math.abs(obj.players[player].body[0] - apple[0])===0)&&(Math.abs(obj.players[player].body[1] - apple[1])===1)||(Math.abs(obj.players[player].body[0] - apple[0])===1)&&(Math.abs(obj.players[player].body[1] - apple[1])===0))?1:0
}
}
for (var i of snake.blackHoles()) {
adjacentBodies+=((Math.abs(i[0] - apple[0])===0)&&(Math.abs(i[1] - apple[1])===1)||(Math.abs(i[0] - apple[0])===1)&&(Math.abs(i[1] - apple[1])===0))?1:0
}
adjacentBodies+=((apple[0]===0)?1:0)+((apple[1]===0)?1:0)+((apple[0]===149)?1:0)+((apple[1]===149)?1:0) //Boundary checks count as walls too.
if (adjacentBodies>=2) {
continue; //Skip this apple. It's dangerous.
}
if (snake.x !== 0 && snake.y !== 0) {
if (snake.me()?.direction !== DOWN && findSafePath(UP, snake.x, snake.y - 1, apple[0], apple[1])) {
snake.setDirection(UP);
return;
}
if (snake.me()?.direction !== RIGHT && findSafePath(LEFT, snake.x - 1, snake.y, apple[0], apple[1])) {
snake.setDirection(LEFT);
return;
}
if (snake.me()?.direction !== LEFT && findSafePath(RIGHT, snake.x + 1, snake.y, apple[0], apple[1])) {
snake.setDirection(RIGHT);
return;
}
if (snake.me()?.direction !== UP && findSafePath(DOWN, snake.x, snake.y + 1, apple[0], apple[1])) {
snake.setDirection(DOWN);
return;
}
}
}
console.log("No viable path, just survive for now...")
TEST_REGIONS = []
EXTRA_MOVEMENT=false
if (snake.x !== 0 && snake.y !== 0) {
if (snake.me()?.direction !== DOWN && findSafePath(UP, snake.x, snake.y - 1, 0, 0)) {
snake.setDirection(UP);
return;
}
if (snake.me()?.direction !== RIGHT && findSafePath(LEFT, snake.x - 1, snake.y, 0, 0)) {
snake.setDirection(LEFT);
return;
}
if (snake.me()?.direction !== LEFT && findSafePath(RIGHT, snake.x + 1, snake.y, 0, 0)) {
snake.setDirection(RIGHT);
return;
}
if (snake.me()?.direction !== UP && findSafePath(DOWN, snake.x, snake.y + 1, 0, 0)) {
snake.setDirection(DOWN);
return;
}
}
TEST_REGIONS = []
EXTRA_MOVEMENT=false
console.log("Last resort...")
if (snake.me()?.direction !== DOWN && findSafePath(UP, snake.x, snake.y - 1, 149, 149)) {
snake.setDirection(UP);
return;
}
if (snake.me()?.direction !== RIGHT && findSafePath(LEFT, snake.x - 1, snake.y, 149, 149)) {
snake.setDirection(LEFT);
return;
}
if (snake.me()?.direction !== LEFT && findSafePath(RIGHT, snake.x + 1, snake.y, 149, 149)) {
snake.setDirection(RIGHT);
return;
}
if (snake.me()?.direction !== UP && findSafePath(DOWN, snake.x, snake.y + 1, 149, 149)) {
snake.setDirection(DOWN);
return;
}
TEST_REGIONS = []
EXTRA_MOVEMENT=false
if (spotIsFree(snake.me().direction, x + (snake.me().direction === RIGHT ? 1 : snake.me().direction === LEFT ? -1 : 0), y + (snake.me().direction === DOWN ? 1 : snake.me().direction === UP ? -1 : 0))) {
return;
} else
if (snake.me()?.direction !== UP && spotIsFree(DOWN, x, y + 1)) {
snake.setDirection(DOWN)
return
} else
if (snake.me()?.direction !== RIGHT && spotIsFree(LEFT, x - 1, y)) {
snake.setDirection(LEFT)
return
} else
if (snake.me()?.direction !== LEFT && spotIsFree(RIGHT, x + 1, y)) {
snake.setDirection(RIGHT)
return
} else
if (snake.me()?.direction !== DOWN && spotIsFree(UP, x, y - 1)) {
snake.setDirection(UP)
return
}
console.log("Dedge.")
}
}
snake.onLogin = function(username) { // When logged in
console.log(`Connected as ${username}!`)
snake.spawn() // Spawn the snake
}
/*
Sample: //Field is 150x150. We'll assume zero-based.
{"players":{"Bot 3263":{"body":[[66,95],[66,96],[66,97],[66,98],[66,99],[66,100],[66,101],[66,102],[66,103],[67,103],[68,103],[69,103],[69,104],[70,104],[70,105],[70,106],[70,107],[71,107],[71,108],[71,109],[71,110],[71,111],[71,112],[72,112],[72,113],[72,114],[72,115],[73,115],[74,115],[75,115],[76,115],[76,116],[76,117],[76,118],[76,119],[76,120],[76,121],[77,121],[77,122],[77,123],[77,124],[77,125],[78,125],[79,125],[80,125],[80,126],[80,127],[80,128],[80,129],[80,130],[80,131],[80,132],[80,133],[80,134],[81,134],[82,134],[82,135],[82,136],[82,137],[82,138],[82,139],[82,140],[83,140],[83,141],[84,141],[85,141],[86,141],[87,141],[88,141],[89,141],[90,141],[91,141],[92,141],[93,141],[94,141],[95,141],[96,141],[97,141],[98,141],[99,141],[100,141],[101,141],[101,142],[100,142],[100,143],[99,143],[98,143],[97,143],[96,143],[95,143],[94,143],[93,143],[92,143],[91,143],[90,143],[89,143],[88,143],[87,143],[86,143],[85,143],[84,143],[83,143],[82,143],[82,144],[82,145],[81,145],[80,145],[79,145],[78,145],[77,145],[76,145],[75,145],[74,145],[73,145],[72,145],[71,145],[70,145],[69,145],[69,146],[68,146],[67,146],[66,146],[65,146],[64,146],[63,146],[62,146],[62,145],[62,144],[62,143],[62,142],[62,141],[62,140],[61,140],[60,140],[60,139],[59,139],[59,138],[59,137],[59,136],[59,135],[59,134],[59,133],[59,132],[59,131],[59,130],[59,129],[59,128],[59,127],[59,126],[59,125],[59,124],[58,124],[58,125],[58,126],[58,127],[58,128],[58,129],[58,130],[58,131],[58,132],[58,133],[58,134],[58,135],[58,136],[58,137],[58,138],[58,139],[57,139],[56,139],[55,139],[54,139],[53,139],[52,139],[51,139],[51,140],[50,140],[49,140],[48,140],[47,140],[47,141],[46,141],[45,141],[44,141],[43,141],[42,141],[42,142],[41,142],[41,143],[40,143],[39,143],[38,143],[38,142],[38,141],[38,140],[38,139],[38,138],[38,137],[38,136],[38,135],[38,134],[38,133],[38,132],[38,131],[38,130],[37,130],[36,130],[36,129],[36,128],[36,127],[35,127],[35,126],[36,126],[37,126],[38,126],[39,126],[39,127],[39,128],[39,129],[39,130],[40,130],[40,129],[40,128],[40,127],[40,126],[40,125],[39,125],[38,125],[37,125],[36,125],[35,125],[34,125],[33,125],[32,125],[32,126],[32,127],[31,127],[30,127],[29,127],[28,127],[27,127],[26,127],[25,127],[24,127],[23,127],[22,127],[21,127],[20,127],[19,127],[18,127],[17,127],[16,127],[15,127],[15,126],[14,126],[14,127],[14,128],[14,129],[14,130],[14,131],[14,132],[13,132],[12,132],[12,133],[11,133],[10,133],[9,133],[9,134],[10,134],[11,134],[12,134],[12,135],[12,136],[12,137],[12,138],[12,139],[12,140],[12,141],[12,142],[12,143],[12,144],[12,145],[12,146],[12,147],[12,148],[13,148],[14,148],[14,149],[13,149],[12,149],[11,149],[11,148],[11,147],[11,146],[11,145],[11,144],[11,143],[11,142],[11,141],[11,140],[11,139],[11,138],[11,137],[11,136],[10,136],[9,136],[9,135],[8,135],[8,134],[8,133],[8,132],[8,131],[8,130],[8,129],[8,128],[8,127],[8,126],[8,125],[8,124],[8,123],[8,122],[8,121],[8,120],[8,119],[8,118],[8,117],[8,116],[8,115],[8,114],[8,113],[8,112],[9,112],[9,111],[9,110],[9,109],[10,109],[11,109],[11,108],[10,108],[9,108],[8,108],[7,108],[7,109],[6,109],[6,110],[5,110],[4,110],[3,110],[3,109],[3,108],[4,108],[4,107],[4,106],[5,106],[5,105],[5,104],[5,103],[6,103],[6,102],[6,101],[7,101]],"head":[7,100],"direction":2,"grow":0,"bc":{"r":57,"g":166,"b":110}},"Bot 2494":{"body":[[58,83],[57,83],[57,82],[56,82],[56,81],[55,81],[54,81],[53,81],[52,81],[51,81],[51,80],[50,80],[50,81],[50,82],[50,83],[50,84],[49,84],[48,84],[48,85],[47,85],[46,85],[45,85],[45,84],[45,83],[45,82],[45,81],[45,80],[45,79],[45,78],[45,77],[45,76],[45,75],[46,75],[46,74],[46,73],[47,73],[47,72],[47,71],[47,70],[47,69],[48,69],[49,69],[49,68],[49,67],[49,66],[49,65],[49,64],[48,64],[47,64],[46,64],[46,63],[45,63],[44,63],[44,62],[43,62],[43,63],[43,64],[43,65],[43,66],[43,67],[43,68],[42,68],[41,68],[40,68],[40,69],[40,70],[40,71],[40,72],[40,73],[40,74],[40,75],[39,75],[38,75],[38,76],[38,77],[38,78],[37,78],[36,78],[35,78],[34,78],[33,78],[32,78],[32,79],[31,79],[31,80],[30,80],[29,80],[28,80],[28,79],[28,78],[28,77],[28,76],[28,75],[28,74],[28,73],[27,73],[26,73],[26,72],[25
*/