From 2bbb7877f91c20b6e4a1cfbec465057e6eab7e02 Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <thefoips@gmail.com>
Date: Sun, 25 Jan 2015 12:14:16 +0000
Subject: [PATCH] Basic Lua player that communicates directly with the server

---
 lua/random_player.lua | 190 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 190 insertions(+)
 create mode 100755 lua/random_player.lua

diff --git a/lua/random_player.lua b/lua/random_player.lua
new file mode 100755
index 0000000..e1e998d
--- /dev/null
+++ b/lua/random_player.lua
@@ -0,0 +1,190 @@
+#! /usr/bin/lua
+
+-- use socket to communicate with the server directly
+require "socket"
+
+-- usage text
+local USAGE = [[
+NAME
+	random_player.lua
+
+USAGE
+	random_player.lua --host=HOSTNAME
+
+SUMMARY
+	Random ttrts player using lua Connects directly to ttrts server
+
+OPTIONS
+	HOSTNAME - host to connect to
+]]
+
+-- [[ Perform the handshake to connect ]]
+ttrts = {}
+do 
+
+ttrts.PerformHandshake = function (sock)
+
+	if not sock then error("Must pass PerformHandshake a socket") end
+
+	local line = sock:receive('*l')
+
+	local player, name = string.match(line, "player (%d+) name ([%a%d]+)")
+
+	-- bail out if handshake information failed
+	assert( player and name , "Handshake failed with incorrect player and name")
+
+	print( "Player: " .. player )
+	print( "Name: " .. name )
+
+	-- return the handshake line with a new line (removed by the *l call to receive) and string end
+	sock:send( line .. "\n" .. "\0" )
+
+	return player, name
+end
+
+-- [[ Recieve gamestate info ]]
+ttrts.GetState = function (newLine)
+
+	if not newLine then error("Must pass GetState a method") end
+
+	local line = ""
+	local state = {}
+
+	-- INVESTIGATE WHY THIS IS NEEDED
+	newLine()
+
+	local thename = newLine()
+	state.name = string.match( thename, "NAME:([%a%d]+)" )
+	assert( state.name, "Gamestate file format missmatch (no name)")
+
+	state.size = {}
+	local sizes = newLine()
+	state.size.x, state.size.y = string.match( sizes, "SIZE:%[(%d+),(%d+)%]" )
+	assert( state.size.x and state.size.y , "Gamestate file format missmatch (size failure)")
+
+	state.turn = string.match( newLine(), "TURN:(%d+)" )
+	assert( state.turn, "Gamestate file format missmatch (no turn)")
+
+	-- Get the wall line
+	state.walls = {}
+	local wallline = newLine()
+
+	assert(string.match( wallline, "WALL:"), "Gamestate file format missmatch (no wall line)")
+
+	-- Grab all walls on the line
+	for wallstring in string.gmatch(wallline, "%[%d+,%d+%]") do 
+		local wall = {}
+		wall.x, wall.y = string.match( wallstring, "%[(%d+),(%d+)%]" )
+		table.insert(state.walls,wall)
+	end
+
+	assert( newLine() == "~~~~", "Gamestate file format missmatch (missing ~~~~)" )
+
+	-- get the units
+	state.units = {}
+
+	local unitline = newLine()
+	while unitline ~= "END" do
+
+		local unit = {}
+		unit.pos = {}
+
+		-- Parse the unit line
+		unit.id, unit.player, unit.vis, unit.dir, unit.pos.x, unit.pos.y = 
+			string.match( unitline, "UNIT:(%d+) pl:(%d+) vs:([^%s]+) dr:(%a+) ps:%[(%d+),(%d+)%]")
+
+		assert(unit.id and unit.player and unit.vis and unit.dir and unit.pos.x and unit.pos.y, "gamestate file format missmatch (error with unit)")
+
+		table.insert(state.units,unit)
+
+		unitline = newLine()
+	end
+
+	assert(unitline == "END", "Gamestate file format missmatch (didn't end in END)")
+
+	return state
+end
+
+-- [[ Get Random Orders from gamestate for a particular player ]]
+ttrts.GetRandomOrders = function( id, state )
+  
+	local orders = ""
+	local possibleorders = { "F", "L", "R", "A" }
+
+	local units = state.units
+	for key,unit in pairs(units) do
+		if unit.id == id then
+			local order = "F" -- TODO MAKE RANDOM
+			orders = orders .. "ORDER:" .. order .. " id:" .. tostring(id) .. "\n"
+		end
+	end
+
+	orders = orders .. "END\n"
+
+	return orders
+end
+
+-- [[ Send the orders to the server through the socket ]]
+ttrts.SendOrders = function( socket, orders )
+	assert( string.match(orders,"END"), "Cannot send orders without END" )
+	socket:send( orders .. "\0" )
+end
+
+end -- end ttrts
+
+-- [[ http://lua-users.org/wiki/AlternativeGetOpt  ]]
+function getopt( arg, options ) 
+	local tab = {} 
+	for k, v in ipairs(arg) do if string.sub( v, 1, 2) == "--" then local x = string.find( v, "=", 1, true ) if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 ) else tab[ string.sub( v, 3 ) ] = true end 
+	elseif string.sub( v, 1, 1 ) == "-" then local y = 2 local l = string.len(v) local jopt while ( y <= l ) do jopt = string.sub( v, y, y ) if string.find( options, jopt, 1, true ) then if y < l then tab[ jopt ] = string.sub( v, y+1 ) y = l else tab[ jopt ] = arg[ k + 1 ] end 
+	else tab[ jopt ] = true end 
+	y = y + 1 end 
+	end end 
+	return tab 
+end
+
+
+-- [[ =================== Program Start ======================= ]]
+
+-- [[ Get our options and set up state ]]
+local opts = getopt(arg, "host")
+
+-- if no host or host not set
+if 	not opts.host 
+	or opts.host == true 
+then 
+	print(USAGE)
+	return
+end
+
+--[[ Attempt to connect to the server ]]
+print( "Connecting to " .. opts.host )
+
+local sock = socket.connect( opts.host, 11715 )
+if not sock then error("Failed to connect to " .. opts.host .. " on port 11715") end
+
+print( "Connected to " .. opts.host )
+
+local function GetNewLineFromSocket() 
+	return sock:receive('*l')
+end
+
+--[[ Perform handshake ]]
+
+-- receive the handshake line
+local player, name = ttrts.PerformHandshake(sock)
+
+--[[ Begin main loop ]]
+while not gameover do
+
+	-- Grab the current gamestate
+	local gamestate = ttrts.GetState( GetNewLineFromSocket )
+
+	-- get the orders
+	local orders = ttrts.GetRandomOrders( player, gamestate )
+
+	-- send the orders
+	ttrts.SendOrders( sock, orders )
+
+	if table.getn( gamestate.units ) == 0 then gameover = true end
+end
\ No newline at end of file