--Prevent standalone execution: if not TRAINSPORTED then print("To prevent players from harm, this file may only be executed by the trAInsported game.") return end --AI by bigiron: trains = {} map = {} passengers = {} cachedRoutes = {} function ai.init(theMap, money) map = theMap buyTrain(1,1, 'E') end function ai.enoughMoney() buyTrain(1,3) end function ai.newPassenger(name, x, y, destX, destY, vipTime) --print(name .. " appeared!") passengers[name] = {name=name, x=x, y=y, destX=destX, destY=destY} routeTo(x, y, destX, destY, "N") -- use this time to cache the route routeTo(x, y, destX, destY, "S") routeTo(x, y, destX, destY, "E") routeTo(x, y, destX, destY, "W") end function ai.passengerBoarded(train, passenger) print(passenger .. " boarded!") -- set the entry in the passengerList for the passenger to nil. This is the accepted way of "deleting" the entry in Lua. passengers[passenger] = nil end function ai.newTrain(train) end function ai.foundPassengers(train, localPass) i = 1 dist = 100 pass = nil if train.passenger then return end for i = 1,#localPass do routeTo(train.x, train.y, localPass[i].destX, localPass[i].destY, train.dir) d = distance(train, localPass[i]) if d < dist then dist = d pass = localPass[i] end end if pass then --print(pass.name .. " boarded!") passengers[pass.name] = nil end return pass end function ai.chooseDirection(train, directions) if train.passenger then return routeTo(train.nextX, train.nextY, train.passenger.destX, train.passenger.destY, train.dir).dir else -- eventually, choose an optimum passenger local dist = 1000 local dir = "" for _, pass in pairs(passengers) do local route = routeTo(train.nextX, train.nextY, pass.x, pass.y, train.dir) if route.path then local dropRoute = routeTo(pass.x, pass.y, pass.destX, pass.destY, string.sub(route.path, -2, -1)) if route.length + dropRoute.length < dist then dist = route.length + dropRoute.length dir = route.dir end used, avail = getNumberOfLines() if avail-used < 1000 then break end end end return dir end end function ai.foundDestination(train) dropPassenger(train) end function getMap(x, y) if map[x] and map[x][y] then return map[x][y] end end function routeTo(initialX, initialY, destX, destY, prevDir) local queue = {} local visited = {} local item table.insert(queue, {x=initialX, y=initialY, dirs="", from=prevDir}) local routeKey = string.format("%d,%d,%d,%d,%s", initialX, initialY, destX, destY, prevDir) item = cachedRoutes[routeKey] if item then --print("Found cached path from "..initialX..","..initialY.." to "..destX..","..destY.." via "..item.path) return item end while #queue > 0 do item = table.remove(queue, 1) local visKey = string.format("%d,%d", item.x, item.y) if not visited[visKey] then visited[visKey] = true --print(string.format("item is at %d,%d with history %s from ", item.x, item.y, item.dirs)) if item.x == destX and item.y == destY then --print("Found path from "..initialX..","..initialY.." to "..destX..","..destY.." via "..item.dirs) local path = {x=initialX, y=initialY, dir=string.sub(item.dirs, 1, 1), path=item.dirs, length=string.len(item.dirs)} memoizePath(path, destX, destY, prevDir) return path end --print(getMap(item.x+1, item.y)) if getMap(item.x+1,item.y) == 'C' and item.from ~= 'E' then table.insert(queue, {x=item.x+1, y=item.y, dirs=item.dirs .. "E", from="W"}) end if getMap(item.x-1,item.y) == 'C' and item.from ~= 'W' then table.insert(queue, {x=item.x-1, y=item.y, dirs=item.dirs .. "W", from="E"}) end if getMap(item.x,item.y+1) == 'C' and item.from ~= 'S' then table.insert(queue, {x=item.x, y=item.y+1, dirs=item.dirs .. "S", from="N"}) end if getMap(item.x,item.y-1) == 'C' and item.from ~= 'N' then table.insert(queue, {x=item.x, y=item.y-1, dirs=item.dirs .. "N", from="S"}) end end end print("Found no path from "..initialX..","..initialY.." to "..destX..","..destY.." starting "..prevDir) return {} end function memoizePath(item, destX, destY, prevDir) local x = item.x local y = item.y local key = string.format("%d,%d,%d,%d,%s", item.x, item.y, destX, destY, prevDir) --print("memoized "..key) cachedRoutes[key] = item local nextX local nextY if item.length >=2 then if item.dir == 'N' then nextX = item.x nextY = item.y - 1 elseif item.dir == 'S' then nextX = item.x nextY = item.y + 1 elseif item.dir == 'E' then nextX = item.x + 1 nextY = item.y elseif item.dir == 'W' then nextX = item.x - 1 nextY = item.y else print("unknown dir "..item.dir) end memoizePath({dir=string.sub(item.path,2,2), path=string.sub(item.path,2,-1), x=nextX, y=nextY, length=string.len(item.path)-1}, destX, destY, item.dir) end end function table.contains(table, element) for _, value in pairs(table) do if value == element then return true end end return false end function distance(train, passenger) return sqrt((train.x-passenger.destX)^2 + (train.y-passenger.destY)^2) end