Skip to content

Instantly share code, notes, and snippets.

@popey456963
Created January 29, 2017 00:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save popey456963/fa8607b414b42a7060a06330654ceb1a to your computer and use it in GitHub Desktop.
Save popey456963/fa8607b414b42a7060a06330654ceb1a to your computer and use it in GitHub Desktop.
JavaScript Wrapper
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
this.lastMap = []
this.lastCities = []
this.firstUpdate = true
}
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) {
if (this.firstUpdate) {
data.lastMap = []
for (var i = 0; i < data.map_diff[2] * data.map_diff[3]; i++) {
data.lastMap.push(0)
}
this.firstUpdate = false
}
data.map = this.patch(this.lastMap, data.map_diff)
this.lastMap = data.map.concat([])
data.cities = this.patch(this.lastCities, data.cities_diff)
this.lastCities = data.cities.concat([])
console.log(JSON.stringify(data))
this.width = parseInt(data.map[0])
this.height = parseInt(data.map[1])
var map_data = this.patch(this.lastMap, 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)
}
})
}
// patch(a, delta(a, b)) === b
Bot.prototype.patch = function(old, diff) {
var out = [];
var i = 0;
while(i < diff.length) {
if (diff[i]) { // matching
Array.prototype.push.apply(out, old.slice(out.length, out.length + diff[i]));
}
i++;
if (i < diff.length && diff[i]) { // mismatching
Array.prototype.push.apply(out, diff.slice(i + 1, i + 1 + diff[i]));
i += diff[i];
}
i++;
}
return out;
}
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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment