Created
December 22, 2016 23:20
-
-
Save popey456963/bff3ae1f1db911b529c50fc82c04fc60 to your computer and use it in GitHub Desktop.
Generals Bot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var chalk = require('chalk') | |
var generate = require('project-name-generator') | |
var Bot = function() { | |
this.pastPlayers = 0 | |
this.playerIndex = 0 | |
this.colours = { | |
"empty": chalk.gray, | |
"mountain": chalk.white, | |
"unknown": chalk.yellow, | |
"friendly": chalk.green, | |
"enemy": chalk.red, | |
"friendlyGeneral": chalk.blue, | |
"enemyGeneral": chalk.magenta, | |
"city": chalk.cyan | |
} | |
this.character = { | |
"empty": "E", | |
"mountain": "M", | |
"unknown": "?", | |
"friendly": "F", | |
"enemy": "B", | |
"friendlyGeneral": "FG", | |
"enemyGeneral": "EG", | |
"city": "C" | |
} | |
this.width = 0 | |
this.height = 0 | |
this.ignoreEvents = ['game_lost', 'game_won', 'queue_update', 'game_start', 'game_update'] | |
this.ourGeneral = [-1, -1] | |
this.generals = [] | |
this.movementOptions = [[-1, 0], [1, 0], [0, -1], [0, 1]] | |
this.attackIndex = 0 | |
} | |
Bot.prototype.connect = function(url) { | |
this.socket = require('socket.io-client')(url, { transports: ['websocket'] }) | |
var onevent = this.socket.onevent | |
this.socket.onevent = function(packet) { | |
var args = packet.data || [] | |
onevent.call(this, packet) | |
packet.data = ["*"].concat(args) | |
onevent.call(this, packet) | |
} | |
} | |
Bot.prototype.play = function(name, user_id, type) { | |
if (type == undefined) { | |
this.socket.emit('play', name, user_id) | |
} else { | |
this.socket.emit('join_private', type, name, user_id) | |
} | |
} | |
Bot.prototype.forceStart = function(start) { | |
this.socket.emit('set_force_start', null, start) | |
} | |
Bot.prototype.onConnect = function(callback) { | |
this.socket.on('connect', callback) | |
} | |
Bot.prototype.onDisconnect = function(callback) { | |
this.socket.on('disconnect', callback) | |
} | |
Bot.prototype.onLose = function(callback) { | |
this.socket.on('game_lost', callback) | |
} | |
Bot.prototype.onWin = function(callback) { | |
this.socket.on('game_won', callback) | |
} | |
Bot.prototype.onQueueUpdate = function(callback) { | |
this.socket.on('queue_update', function(playerCount) { | |
if (playerCount != this.pastPlayers) { | |
callback(playerCount) | |
this.pastPlayers = playerCount | |
} | |
}) | |
} | |
Bot.prototype.onGameStart = function(callback) { | |
this.socket.on('game_start', function(data) { | |
Bot.playerIndex = data.playerIndex | |
callback(data) | |
}) | |
} | |
Bot.prototype.onChat = function(callback) { | |
this.socket.on('chat_message', function(room, data) { | |
callback(data) | |
}) | |
} | |
Bot.prototype.onGameUpdate = function(callback) { | |
this.socket.on('game_update', function(data) { | |
console.log(JSON.stringify(data)) | |
grjaeio | |
this.width = parseInt(data.map[0]) | |
this.height = parseInt(data.map[1]) | |
var map_data = data.map.slice(2, 2 + this.width * this.height) | |
var map = [] | |
var mountain_data = data.map.slice(2 + this.width * this.height, 3 + 2 * this.width * this.height) | |
var counter = 0 | |
for (var j = 0; j < this.height; j++) { | |
var line = [] | |
for (var i = 0; i < this.width; i++) { | |
line.push([map_data[counter]]) | |
counter++ | |
} | |
map.push(line) | |
} | |
var counter = 0 | |
var y = 0 | |
for (var j = 0; j < this.height; j++) { | |
var x = 0 | |
for (var i = 0; i < this.width; i++) { | |
if (mountain_data[counter] == -1) { | |
map[y][x].push("empty") | |
} else if (mountain_data[counter] == -2 || mountain_data[counter] == -4) { | |
map[y][x].push("mountain") | |
} else if (mountain_data[counter] == -3) { | |
map[y][x].push("unknown") | |
} else if (mountain_data[counter] == Bot.playerIndex) { | |
map[y][x].push("friendly") | |
} else { | |
map[y][x].push("enemy") | |
} | |
counter++ | |
x++ | |
} | |
y++ | |
} | |
this.generals = [] | |
for (var i = 0; i < data.generals.length; i++) { | |
if (data.generals[i] != -1 && i != Bot.playerIndex) { | |
var generalXY = this.toXY(data.generals[i]) | |
if (i == Bot.playerIndex) { | |
map[generalXY[1]][generalXY[0]][1] = "friendlyGeneral" | |
this.ourGeneral = generalXY | |
} else { | |
map[generalXY[1]][generalXY[0]][1] = "enemyGeneral" | |
this.generals.push(generalXY) | |
} | |
} | |
} | |
for (var i = 0; i < data.cities.length; i++) { | |
if (data.cities[i] != -1) { | |
var mapXY = this.toXY(data.cities[i]) | |
map[mapXY[1]][mapXY[0]][1] = "city" | |
} | |
} | |
callback(data.turn, map) | |
}.bind(this)) | |
} | |
Bot.prototype.onOther = function(callback) { | |
var that = this | |
this.socket.on("*", function(event, data) { | |
if(that.ignoreEvents.indexOf(event) == -1) { | |
callback(event, data) | |
} | |
}) | |
} | |
Bot.prototype.attack = function(from, to) { | |
var from = this.toPoint(from[0], from[1]) | |
var to = this.toPoint(to[0], to[1]) | |
// console.log(this.attackIndex) | |
console.log("Attacking point", to, "with army from", from) | |
this.socket.emit('attack', from, to, false, this.attackIndex) | |
this.attackIndex++ | |
} | |
Bot.prototype.printMap = function(map) { | |
for (var y = 0; y < map.length; y++) { | |
for (var x = 0; x < map[y].length; x++) { | |
var mapValue = map[y][x][0] | |
var mapType = map[y][x][1] | |
if (mapValue != 0) { | |
map[y][x] = this.colours[mapType]((mapValue + " ").substring(0, 4)) | |
} | |
else { | |
map[y][x] = this.colours[mapType]((this.character[mapType] + " ").substring(0, 4)) | |
} | |
} | |
console.log(map[y].join("")) | |
} | |
} | |
Bot.prototype.frontDistance = function(map) { | |
var frontDistance = [...Array(map.length).keys()].map(i => Array(map[0].length)) | |
for (var y = 0; y < map.length; y++) { | |
for (var x = 0; x < map[y].length; x++) { | |
var mapValue = map[y][x][0] | |
var mapType = map[y][x][1] | |
if (mapType == 'empty' || mapType == 'unknown' || mapType == 'enemy' || mapType == 'enemyGeneral') { | |
frontDistance[y][x] = 1 | |
} else if (mapType == 'friendly' || mapType == 'friendlyGeneral') { | |
frontDistance[y][x] = -1 | |
} else { | |
frontDistance[y][x] = -2 | |
} | |
} | |
} | |
var emptySquares = this.countValues(frontDistance, -1) | |
var counter = 1 | |
var more = true | |
while (emptySquares) { | |
for (var y = 0; y < map.length; y++) { | |
for (var x = 0; x < map[y].length; x++) { | |
if (frontDistance[y][x] == -1) { | |
// console.log("Found our square at: " + x + ", " + y) | |
var around = false | |
for (var i = 0; i < this.movementOptions.length; i++) { | |
var newCoords = [x + this.movementOptions[i][0], y + this.movementOptions[i][1]] | |
if (this.inBounds(newCoords[0], newCoords[1])) { | |
if (frontDistance[newCoords[1]][newCoords[0]] == counter) { | |
around = true | |
break | |
} | |
} | |
} | |
if (around) { | |
// console.log("Incrementing it to: " + (counter + 1)) | |
frontDistance[y][x] = counter + 1 | |
} | |
} | |
} | |
} | |
var emptySquares = this.countValues(frontDistance, -1) | |
if (counter > 100) { | |
emptySquares = 0 | |
more = false | |
} | |
counter++ | |
} | |
// console.log(frontDistance) | |
return [more, frontDistance] | |
} | |
Bot.prototype.findLowestHeight = function(frontDistance, map) { | |
lowestHeight = [9999, [-1, -1], [-1, -1]] | |
for (var y = 0; y < map.length; y++) { | |
for (var x = 0; x < map[y].length; x++) { | |
var mapValue = map[y][x][0] | |
var mapType = map[y][x][1] | |
if (mapValue > 1 && (mapType == "friendly" || mapType == "friendlyGeneral")) { | |
if (frontDistance[y][x] < lowestHeight[0]) { | |
var neighbour = [-1, -1] | |
for (var i = 0; i < this.movementOptions.length; i++) { | |
var newCoords = [x + this.movementOptions[i][0], y + this.movementOptions[i][1]] | |
if (this.inBounds(newCoords[0], newCoords[1])) { | |
// console.log(newCoords) | |
// console.log(frontDistance[newCoords[1]][newCoords[0]]) | |
if (frontDistance[newCoords[1]][newCoords[0]] == frontDistance[y][x] - 1) { | |
if (map[newCoords[1]][newCoords[0]][1] == "enemy" || map[newCoords[1]][newCoords[0]][1] == "enemyGeneral") { | |
if (map[y][x][0] - 1 > map[newCoords[1]][newCoords[0]][0]) { | |
// console.log("Setting Neighbour") | |
neighbour = newCoords | |
} | |
} else { | |
neighbour = newCoords | |
} | |
} | |
} | |
} | |
if (!this.arrayEquals(neighbour, [-1, -1])) { | |
lowestHeight = [frontDistance[y][x], [x, y], neighbour] | |
} | |
} | |
} | |
} | |
} | |
return lowestHeight | |
} | |
// TODO: IMPLEMENT | |
Bot.prototype.collectAround = function(x, y) { | |
console.log("Collecting around: " + x + ", " + y) | |
} | |
Bot.prototype.toPoint = function(x, y) { | |
return y * this.width + x | |
} | |
Bot.prototype.toXY = function(point) { | |
return [point % this.width, Math.floor(point / this.width)] | |
} | |
Bot.prototype.toGeneral = function(x, y) { | |
x = x - this.ourGeneral[0] | |
} | |
Bot.prototype.inBounds = function(x, y) { | |
if (x < 0 || y < 0 || x >= this.width || y >= this.height) { | |
return false | |
} | |
return true | |
} | |
Bot.prototype.randomID = function() { | |
return (Math.random().toString(36)+'00000000000000000').slice(2, 11) | |
} | |
Bot.prototype.randomName = function() { | |
return generate().spaced.replace(/\w\S*/g, function(x){return x.charAt(0).toUpperCase() + x.substr(1).toLowerCase()}) | |
} | |
Bot.prototype.add = function(a, b) { | |
return a + b | |
} | |
Bot.prototype.arrayEquals = function(a, b) { | |
if (a === b) return true | |
if (a == null || b == null) return false | |
if (a.length != b.length) return false | |
for (var i = 0; i < a.length; ++i) { | |
if (a[i] !== b[i]) return false | |
} | |
return true | |
} | |
Bot.prototype.countValues = function(array, value) { | |
return array.map(function(row) { | |
return row.reduce(function(n, val) { | |
return n + (val == value) | |
}, 0) | |
}).reduce(this.add, 0) | |
} | |
Bot.prototype.shuffle = function(array) { | |
var currentIndex = array.length, temporaryValue, randomIndex | |
while (0 !== currentIndex) { | |
randomIndex = Math.floor(Math.random() * currentIndex) | |
currentIndex -= 1 | |
temporaryValue = array[currentIndex] | |
array[currentIndex] = array[randomIndex] | |
array[randomIndex] = temporaryValue | |
} | |
return array | |
} | |
module.exports = new Bot() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Bot = require('./bot.js') | |
var fs = require('fs') | |
var Log = require('log') | |
var log = new Log('debug', fs.createWriteStream('special_bot.log')) | |
Bot.connect('http://localhost:8080') | |
// Bot.connect('http://euws.generals.io') | |
log.info("Bot Connected...") | |
Bot.onConnect(function() { | |
console.log('Connected Successfully') | |
// Bot.play(Bot.randomName(), Bot.randomID()) | |
Bot.play(Bot.randomName(), 'special_bot') | |
Bot.forceStart(true) | |
log.info("Joined Queue") | |
}) | |
Bot.onDisconnect(function() { | |
console.log('Disconnected') | |
log.info("Disconnected from Server") | |
}) | |
Bot.onLose(function() { | |
console.log('We Lost!') | |
log.info("We Lost!") | |
Bot.play(Bot.randomName(), 'special_bot') | |
Bot.forceStart(true) | |
log.info("Joined Queue") | |
}) | |
Bot.onWin(function() { | |
console.log('We Won!') | |
log.info("We Won!") | |
Bot.play(Bot.randomName(), 'special_bot') | |
Bot.forceStart(true) | |
log.info("Joined Queue") | |
}) | |
Bot.onQueueUpdate(function(playerCount) { | |
console.log('We have ' + playerCount + ' people in our current queue.') | |
}) | |
Bot.onChat(function(message) { | |
if (message.username) { | |
console.log(message.username + ": " + message.text) | |
} else { | |
console.log(message.text) | |
} | |
}) | |
Bot.onGameStart(function(data) { | |
console.log('===== A game has started! =====') | |
console.log('Player Index: ' + data.playerIndex) | |
console.log('Replay ID: ' + data.replay_id) | |
console.log('Players: ' + data.usernames.join(', ')) | |
log.info('===== A game has started! =====') | |
log.info('Player Index: ' + data.playerIndex) | |
log.info('Replay ID: ' + data.replay_id) | |
log.info('Players: ' + data.usernames.join(', ')) | |
}) | |
Bot.onGameUpdate(function(turn, map) { | |
if (turn) { | |
// console.log('===== Game Turn ' + turn + ' =====') | |
var [move, frontDistance] = Bot.frontDistance(map) | |
if (move) { | |
var lowestHeight = Bot.findLowestHeight(frontDistance, map) | |
if (!Bot.arrayEquals(lowestHeight[1], [-1, -1]) && !Bot.arrayEquals(lowestHeight[2], [-1, -1])) { | |
Bot.attack(lowestHeight[1], lowestHeight[2]) | |
} | |
} | |
// Bot.printMap(map) | |
} | |
}) | |
Bot.onOther(function(event, data) { | |
console.log('Got unhandled event: ' + event) | |
console.log('With data: ' + JSON.stringify(data)) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment