Skip to content

Instantly share code, notes, and snippets.

@popey456963
Created January 29, 2017 00:12
Show Gist options
  • Select an option

  • Save popey456963/fa8607b414b42a7060a06330654ceb1a to your computer and use it in GitHub Desktop.

Select an option

Save popey456963/fa8607b414b42a7060a06330654ceb1a to your computer and use it in GitHub Desktop.

Revisions

  1. popey456963 created this gist Jan 29, 2017.
    397 changes: 397 additions & 0 deletions bot.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,397 @@
    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()