Jump to content

How do I make a rover drive itself intelligently?


MAFman

Recommended Posts

I know I've posted A LOT on this, but I'm getting really frustrated.

I want to make a rover using kOS drive in a certain general direction until it can no longer connect to KSC with RemoteTech, then drive the other way until it connects again.

I want to do all this while actively avoiding slopes greater than (+ -)10°.

How do i do this? :mad:

Link to comment
Share on other sites

Moving to Gameplay Questions.

It all sounded pretty straightforward until you tacked on that last sentence. Navigation across terrain is a *hard* problem.

Leaving out the slope issue: I don't know anything about kOS, but is it safe to assume that it has functions for your lat/long, the lat/long of KSC, the current heading, and trig functions? If so, why wouldn't you just do this? 1. Calculate the bearing from current position to KSC, by doing some trig on the two lat/long coords. 2. Compare that with current bearing. 3. If they're not close, turn left or right until they are.

Link to comment
Share on other sites

1 minute ago, Snark said:

Moving to Gameplay Questions.

It all sounded pretty straightforward until you tacked on that last sentence. Navigation across terrain is a *hard* problem.

Leaving out the slope issue: I don't know anything about kOS, but is it safe to assume that it has functions for your lat/long, the lat/long of KSC, the current heading, and trig functions? If so, why wouldn't you just do this? 1. Calculate the bearing from current position to KSC, by doing some trig on the two lat/long coords. 2. Compare that with current bearing. 3. If they're not close, turn left or right until they are.

KOS does have information for the ship's position, as well as all kinds of math functions.

Also, what exactly is bearing?

Link to comment
Share on other sites

8 minutes ago, MAFman said:

KOS does have information for the ship's position, as well as all kinds of math functions.

Also, what exactly is bearing?

So what exactly are you looking for, then? I assume that the above isn't enough to answer your question, but am not sure what the missing ingredient is. I guess part of my confusion is that I'm not sure whether you're asking an algorithm question, a math question, or a kOS programming question.

By "bearing" (or "heading", or "azimuth", if you prefer), I just mean "what direction is the rover pointed", expressed as an angle.

Link to comment
Share on other sites

2 minutes ago, Snark said:

So what exactly are you looking for, then? I assume that the above isn't enough to answer your question, but am not sure what the missing ingredient is.

By "bearing" (or "heading", or "azimuth", if you prefer), I just mean "what direction is the rover pointed", expressed as an angle.

I'm looking for a script that runs on bootup and sets my RemoteTech network up automagically, i.e. without me actually doing anything except hitting launch.

The first rover would start at KSC, then drive west until it ran out of connection, then drive east until it had connection again.

Every subsequent rover would follow suit, only driving west until the hit ocean, then north until ocean, then south until ocean, you get the idea.

Link to comment
Share on other sites

Is there a way to implement the a* search algorithm in kOS, perhaps using kerbalmaps.com? If so, how would I adapt the algorithm to say, "keep navigating until you run out of connection, then navigate backwards until you gain connection."?

Link to comment
Share on other sites

I modified my script a bit, and now it seems to insist on driving into the ocean, or crashing... Halp!

How do I get it to select the point from a list with the minimum slope and angle between itself and the desired heading?

Link to comment
Share on other sites

I modified my script...AGAIN... but now it's throwing an error...

Boot:

// First, set the ship to a known configuration.
SET SHIP:CONTROL:PILOTMAINTHROTTLE TO 0.
LIGHTS OFF.
GEAR OFF.
FOR p IN SHIP:PARTS
{
	IF	p:TITLE = "RoveMax Model M1" OR
		p:TITLE = "RoveMax Model S2" OR
		p:TITLE = "RoveMax Model XL3" OR
		p:TITLE = "TR-2L Ruggedized Vehicular Wheel"
	{
		SET SHIP:TYPE TO "rover".
	}
}
IF SHIP:TYPE = "rover"
{
	BRAKES ON.
	
	WHEN SHIP:SENSORS:LIGHT < 0.75 THEN
	{
		LIGHTS ON.
		PRESERVE.
	}
	WHEN SHIP:SENSORS:LIGHT > 0.75 THEN
	{
		LIGHTS OFF.
		PRESERVE.
	}
	
	FOR p IN SHIP:PARTS
	{
		IF p:NAME = "fuelCell" OR p:NAME = "fuelCellArray"
		{
			p:GETMODULE("ModuleResourceConverter"):DOACTION("Start Fuel Cell", TRUE).
		}
	}
}
ELSE
{
	BRAKES OFF.
}

SAS OFF.
RCS OFF.

FUNCTION NOTIFY
{
	PARAMETER message, priority.
	IF priority = "NOMINAL"
	{
		SET col TO GREEN.
	}
	ELSE IF priority = "ALERT"
	{
		SET col TO RGB((255/255), (114/255), 0). // Safety orange
	}
	ELSE IF priority = "WARNING"
	{
		SET col TO RED.
	}
	HUDTEXT("kOS: " + message, 2, 2, 30, col, FALSE).
}

FUNCTION ON_DRIVE
{
	PARAMETER n, vol.
	SWITCH TO vol. LIST FILES IN allFiles.
	FOR f IN allFiles
	{
		IF f:NAME = n
		{
			SWITCH TO 1. RETURN TRUE.
		}
	}
	SWITCH TO 1. RETURN FALSE.
}
FUNCTION DELAY
{
	SET dTime TO ADDONS:RT:DELAY(SHIP).
	SET accTime TO 0. // Accumulated time
	UNTIL accTime >= dTime
	{
		SET start TO TIME:SECONDS.
		WAIT UNTIL (TIME:SECONDS - start) > (dTime - accTime) OR NOT ADDONS:RT:HASCONNECTION(SHIP).
		SET accTime TO accTime + TIME:SECONDS - start.
	}
}
// Get a file from KSC
FUNCTION DOWNLOAD
{
	PARAMETER f.
	DELAY().
	IF ON_DRIVE(f, 1)
	{
		DELETE f.
	}
	IF ON_DRIVE(f, 0)
	{
		SWITCH TO 0.
		COPY f TO 1.
		SWITCH TO 1.
	}
}
// Put a file on KSC
FUNCTION UPLOAD
{
	PARAMETER f.
	DELAY().
	IF ON_DRIVE(f, 0)
	{
		SWITCH TO 0. DELETE f. SWITCH TO 1.
	}
	IF ON_DRIVE(f, 1)
	{
		COPY f TO 0.
	}
}
// Run a library, downloading it from KSC if necessary
FUNCTION REQUIRE
{
	PARAMETER lib.
	IF NOT ON_DRIVE(lib, 1)
	{
		DOWNLOAD(lib).
	}
	RENAME lib TO "new_library.ks".
	RUN new_library.
	RENAME "new_library.ks" TO lib.
}
// THE ACTUAL BOOTUP PROCESS
SET updateScript TO SHIP:NAME + ".update.ks".
// If we have a connection, see if there are new instructions. If so, download and run them.
IF ADDONS:RT:HASCONNECTION(SHIP)
{
	IF ON_DRIVE(updateScript, 0) // If the update script exists on the archive, download and run it.
	{
		IF ON_DRIVE("update.ks", 1) // If an old file exists on the archive
		{
			NOTIFY("An old update file already exists. Deleting old file...", "ALERT").
			WAIT 5.
			DELETE update.ks.
		}
		DOWNLOAD(updateScript).
		SWITCH TO 0. DELETE updateScript. SWITCH TO 1.
		// Whichever situation is true, we now have an up-to-date file
		NOTIFY("Update file found! Executing...", "NOMINAL").
		WAIT 5.
		RENAME updateScript TO "update.ks".
		RUN update.ks.
		DELETE update.ks.
	}
	ELSE // We don't have an update script, so do nothing.
	{
		NOTIFY("No update file found. Either you haven't created one or one is not necessary.", "ALERT").
	}
	// If a startup.ks file exists on the disk, run that.
	SET bootScript TO SHIP:NAME + ".startup.ks".
	IF ON_DRIVE(bootScript, 1)
	{
		SWITCH TO 1. DELETE bootScript. // If we already have an old startup file, delete it.
		IF ON_DRIVE(startup.ks, 1)
		{
			SWITCH TO 1.
			DELETE startup.ks.
		}
		IF ON_DRIVE(bootScript, 0)
		{
			DOWNLOAD(bootScript).
			RENAME bootScript TO "startup.ks".
			RUN startup.ks.
		}
		ELSE
		{
			NOTIFY("Startup failed! Instructions not found.", "WARNING"). WAIT 5. REBOOT.
		}
	}
	ELSE IF ON_DRIVE(bootScript, 0)
	{
		IF NOT ON_DRIVE(bootScript, 1)
		{
			DOWNLOAD(bootScript).
			RENAME bootScript TO "startup.ks".
			RUN startup.ks.
			DELETE startup.ks.
		}
		ELSE
		{
			DELETE bootScript.
			DOWNLOAD(bootScript).
			RENAME bootScript TO "startup.ks".
			RUN startup.ks.
			DELETE startup.ks.
		}
	}
}
ELSE
{
	WAIT UNTIL ADDONS:RT:HASCONNECTION(SHIP).	
	// Avoid thrashing the CPU (when no startup.ks, but we have a persistent connection, it will continually reboot)
	WAIT 5.
	REBOOT.
}

Startup:

// Relay rover startup file
// Lists the number of active "relay" rovers
// and bases its navigation software on that.
SWITCH TO 1. DELETE boot.ks.

// Download the appropriate libraries
DOWNLOAD(autoNav.ks).

// List the active relays
LIST TARGETS IN allVessels.
SET relays TO LIST().
FOR t IN allVessels
{
	IF t:NAME = "Relay" AND t:TYPE = "rover"
	{
		relays:ADD(t).
	}
}
// Decide which direction to go
IF relays:LENGTH = 0
{
	RUN autonav(270).
}
ELSE
{
	SET iterationNum TO relays:LENGTH + 1.
	IF iterationNum = 1
	{
		RUN autonav(0).
	}
	ELSE IF iterationNum = 2
	{
		RUN autonav(270).
	}
	ELSE IF iterationNum = 3
	{
		RUN autonav(180).
	}
	ELSE
	{
		SET iterationNum TO MOD(iterationNum, 3) + 1. // It can be 1, 2, or 3. No more, no less.
	}
}

Autonav:

PARAMETER direct.
SWITCH TO 1. DELETE startup.ks.
UNTIL NOT ADDONS:RT:HASCONNECTION(SHIP)
{
	POPULATE(direct).
	SEARCH(options).
	DRIVE_TO(nextPoint).
}
UNTIL ADDONS:RT:HASCONNECTION(SHIP)
{
	POPULATE(direct + 180).
	SEARCH(options).
	DRIVE_TO(nextPoint).
}

FUNCTION POPULATE
{
	PARAMETER whichWay.
	SET here TO SHIP:GEOPOSITION.
	
	SET offSet TO (360 * 100) / (2 * CONSTANT:PI * BODY:RADIUS).
	
	SET preOptions TO LIST // An arc 270 degrees wide, 100m in radius
	(
		LATLNG(here:LAT + offset, here:LNG),										// forward
		LATLNG(here:LAT + (SIN(22.5) * offset), here:LNG + (COS(22.5) * offset)),	// 22.5 deg. left
		LATLNG(here:LAT + (SIN(22.5) * offset), here:LNG - (COS(22.5) * offset)),	// 22.5 deg. right
		LATLNG(here:LAT + (SIN(45) * offset), here:LNG + (COS(45) * offset)),		// 45 deg. left
		LATLNG(here:LAT + (SIN(45) * offset), here:LNG - (COS(45) * offset)),		// 45 deg. right
		LATLNG(here:LAT + (SIN(57.5) * offset), here:LNG + (COS(57.5) * offset)),	// 57.5 deg. left
		LATLNG(here:LAT + (SIN(57.5) * offset), here:LNG - (COS(57.5) * offset)),	// 57.5 deg. right
		LATLNG(here:LAT + (SIN(90) * offset), here:LNG + (COS(90) * offset)),		// 90 deg. left
		LATLNG(here:LAT + (SIN(90) * offset), here:LNG - (COS(90) * offset)),		// 90 deg. right
		LATLNG(here:LAT + (SIN(112.5) * offset), here:LNG + (COS(112.5) * offset)),	// 112.5 deg. left
		LATLNG(here:LAT + (SIN(112.5) * offset), here:LNG - (COS(112.5) * offset)),	// 112.5 deg. right
		LATLNG(here:LAT + (SIN(135) * offset), here:LNG + (COS(135) * offset)),		// 135 deg. left
		LATLNG(here:LAT + (SIN(135) * offset), here:LNG - (COS(135) * offset))		// 135 deg. right
	).
	
	// Now rotate the whole thing until it aligns with "whichWay"
	SET options TO LIST().
	FOR o IN preOptions
	{
		// Do some matrix magic
		options:ADD(LATLNG((o:LAT * SIN(whichWay)) - (o:LNG * COS(whichWay)), (o:LAT * COS(whichWay)) + (o:LNG * SIN(whichWay)))).
	}
	RETURN options.
}

FUNCTION SEARCH
{
	PARAMETER options.
	SET sortedBySlope TO LIST().
	FOR o IN options
	{
		// Sort the options by slope, with lowest first.
		SET here TO SHIP:GEOPOSITION.
		SET there TO o.
		SET frac TO (here:TERRAINHEIGHT - there:TERRAINHEIGHT) / 100.
		SET slope_a TO ABS(ARCTAN(frac)).
		
		SET there_last TO . // The option before this one
		SET frac_b TO (here:TERRAINHEIGHT - there_last:TERRAINHEIGHT) / 100.
		SET slope_b TO ABS(ARCTAN(frac_b)).
		
		IF slope_a < slope_b // If the current slope is less than the last slope,
		{
			sortedBySlope:INSERT(0, there). // Put it into the list at the zero-th position
		}
		ELSE // If the current slope is greater than the last slope,
		{
			sortedBySlope:INSERT(0, there_last). // Put the last slope into the list at the zero-th position
		}
	}
	
	SET sortedByAngle TO LIST().
	FOR s IN sortedBySlope
	{
		// Sort the options again, this time by angle between the point and the direction
		SET currentPoint TO s.
		
		SET lastPoint TO . // The option before this one
		SET theta TO ABS(whichWay - currentPoint:HEADING).
		SET last_theta TO ABS(whichWay - lastPoint:HEADING).
		
		IF theta < last_theta
		{
			sortedByAngle:INSERT(0, currentPoint).
		}
		ELSE
		{
			sortedByAngle:INSERT(0, lastPoint).
		}
	}
	
	// Now run through both lists, and pick the first point whose index is the same in both
	
	// If the above condition cannot be met, pick the first point on sortedBySlope().
	
	// Magic! :D
	// I really hope this works...
	FOR this IN sortedBySlope
	{
		SET that TO sortedByAngle[this:INDEX].
		
		IF this:INDEX = that:INDEX
		{
			SET nextPoint TO sortedBySlope[this:INDEX].
			RETURN nextPoint.
		}
	}
	SET nextPoint TO sortedBySlope[0].
	RETURN nextPoint.
}

FUNCTION DRIVE_TO
{
	PARAMETER point.
	SET steerPID TO PIDLOOP().
	SET throttlePID TO PIDLOOP().
	SET steerPID:SETPOINT TO point:HEADING.
	SET throttlePID:SETPOINT TO 2.5.
	UNTIL point:DISTANCE < 10
	{
		SET WHEELTHROTTLE TO throttlePID:UPDATE(TIME:SECONDS, GROUNDSPEED).
		SET WHEELSTEER TO steerPID:UPDATE(TIME:SECONDS, SHIP:HEADING).
	}
	STOP().
}

FUNCTION STOP
{	
	SET WHEELSTEER TO 0.
	SET WHEELTHROTTLE TO 0.
	IF NOT BRAKES
	{
		BRAKES ON.
	}
	WAIT UNTIL GROUNDSPEED < 0.1.
	AG1 OFF. NOTIFY("Arrived!", "NOMINAL"). // Turn the cherry light off and tell us we've arrived
}

FUNCTION PARK
{
	STOP().
	// Activate all non-dipole antennas, and deactivate all dipoles
	FOR p IN SHIP:PARTS
	{
		SET mods TO p:MODULES.
		FOR m IN mods
		{
			IF m:NAME = "ModuleRTAntenna" AND p:NAME <> "RTShortAntenna1"
			{
				m:DOEVENT("activate").
			}
			ELSE IF m:NAME = "ModuleRTAntenna" AND p:NAME = "RTShortAntenna1"
			{
				m:DOEVENT("deactivate").
			}
		}
	}
}

It's saying I can't get the index of a LATLNG, although I'm trying to get the index of a list value, which just so happens to be a LATLNG().

Link to comment
Share on other sites

Improvement? I didn't change the other scripts; just the way I iterate over the lists.

PARAMETER direct.
SWITCH TO 1. DELETE startup.ks.
UNTIL NOT ADDONS:RT:HASCONNECTION(SHIP)
{
	POPULATE(direct).
	SEARCH(options).
	DRIVE_TO(nextPoint).
}
UNTIL ADDONS:RT:HASCONNECTION(SHIP)
{
	POPULATE(direct + 180).
	SEARCH(options).
	DRIVE_TO(nextPoint).
}

FUNCTION POPULATE
{
	PARAMETER whichWay.
	SET here TO SHIP:GEOPOSITION.
	
	SET offSet TO (360 * 100) / (2 * CONSTANT:PI * BODY:RADIUS).
	
	SET preOptions TO LIST // An arc 270 degrees wide, 100m in radius
	(
		LATLNG(here:LAT + offset, here:LNG),										// forward
		LATLNG(here:LAT + (SIN(22.5) * offset), here:LNG + (COS(22.5) * offset)),	// 22.5 deg. left
		LATLNG(here:LAT + (SIN(22.5) * offset), here:LNG - (COS(22.5) * offset)),	// 22.5 deg. right
		LATLNG(here:LAT + (SIN(45) * offset), here:LNG + (COS(45) * offset)),		// 45 deg. left
		LATLNG(here:LAT + (SIN(45) * offset), here:LNG - (COS(45) * offset)),		// 45 deg. right
		LATLNG(here:LAT + (SIN(57.5) * offset), here:LNG + (COS(57.5) * offset)),	// 57.5 deg. left
		LATLNG(here:LAT + (SIN(57.5) * offset), here:LNG - (COS(57.5) * offset)),	// 57.5 deg. right
		LATLNG(here:LAT + (SIN(90) * offset), here:LNG + (COS(90) * offset)),		// 90 deg. left
		LATLNG(here:LAT + (SIN(90) * offset), here:LNG - (COS(90) * offset)),		// 90 deg. right
		LATLNG(here:LAT + (SIN(112.5) * offset), here:LNG + (COS(112.5) * offset)),	// 112.5 deg. left
		LATLNG(here:LAT + (SIN(112.5) * offset), here:LNG - (COS(112.5) * offset)),	// 112.5 deg. right
		LATLNG(here:LAT + (SIN(135) * offset), here:LNG + (COS(135) * offset)),		// 135 deg. left
		LATLNG(here:LAT + (SIN(135) * offset), here:LNG - (COS(135) * offset))		// 135 deg. right
	).
	
	// Now rotate the whole thing until it aligns with "whichWay"
	SET options TO LIST().
	FOR o IN preOptions
	{
		// Do some matrix magic
		options:ADD(LATLNG((o:LAT * SIN(whichWay)) - (o:LNG * COS(whichWay)), (o:LAT * COS(whichWay)) + (o:LNG * SIN(whichWay)))).
	}
	RETURN options.
}

FUNCTION SEARCH
{
	PARAMETER options.
	SET sortedBySlope TO LIST().
	
	SET start TO 0.
	UNTIL start > options:LENGTH
	{
		SET here TO SHIP:GEOPOSITION.
		SET there TO options[start].
		SET frac TO (here:TERRAINHEIGHT - there:TERRAINHEIGHT) / 100.
		SET slope_a TO ABS(ARCTAN(frac)).
		
		SET there_last TO options[start - 1]. // The option before this one
		IF there_last:INDEX > 0
		{
			SET frac_b TO (here:TERRAINHEIGHT - there_last:TERRAINHEIGHT) / 100.
			SET slope_b TO ABS(ARCTAN(frac_b)).
		
			IF slope_a < slope_b // If the current slope is less than the last slope,
			{
				sortedBySlope:INSERT(0, there). // Put it into the list at the zero-th position
			}
			ELSE // If the current slope is greater than the last slope,
			{
				sortedBySlope:INSERT(0, there_last). // Put the last slope into the list at the zero-th position
			}
			SET start TO start + 1.
		}
		ELSE
		{
			SET start TO start + 1.
		}
	}
	
	SET sortedByAngle TO LIST().
	SET start TO 0.
	UNTIL start > sortedBySlope:LENGTH - 1
	{
		// Sort the options again, this time by angle between the point and the direction
		SET currentPoint TO sortedBySlope[startAgain].
		
		SET lastPoint TO sortedBySlope[startAgain - 1]. // The option before this one
		
		IF currentPoint:INDEX > 0
		{
			SET theta TO ABS(whichWay - currentPoint:HEADING).
			SET last_theta TO ABS(whichWay - lastPoint:HEADING).
			
			IF theta < last_theta
			{
				sortedByAngle:INSERT(0, currentPoint).
			}
			ELSE
			{
				sortedByAngle:INSERT(0, lastPoint).
			}
		}
		
		SET start TO startAgain + 1.
	}
	
	// Run through both lists, and if there is a match,
	// i.e. there is a point whose corresponding point
	// in the other list is at the same index,
	// set the next waypoint to the match
	SET ind TO 0.
	UNTIL ind > options:LENGTH
	{
		SET a TO sortedBySlope[ind].
		SET b TO sortedByAngle[ind].
		IF a = b
		{
			SET nextPoint TO sortedBySlope[ind].
			RETURN nextPoint.
		}
		ELSE
		{
			SET ind TO ind + 1.
		}
	}
	SET nextPoint TO sortedBySlope[0]. // If none of the conditions are true, just go with the point with the lowest slope...
	RETURN nextPoint.
}

FUNCTION DRIVE_TO
{
	PARAMETER point.
	SET steerPID TO PIDLOOP().
	SET throttlePID TO PIDLOOP().
	SET steerPID:SETPOINT TO point:HEADING.
	SET throttlePID:SETPOINT TO 2.5.
	UNTIL point:DISTANCE < 10
	{
		SET WHEELTHROTTLE TO throttlePID:UPDATE(TIME:SECONDS, GROUNDSPEED).
		SET WHEELSTEER TO steerPID:UPDATE(TIME:SECONDS, SHIP:HEADING).
	}
	STOP().
}

FUNCTION STOP
{	
	SET WHEELSTEER TO 0.
	SET WHEELTHROTTLE TO 0.
	IF NOT BRAKES
	{
		BRAKES ON.
	}
	WAIT UNTIL GROUNDSPEED < 0.1.
	AG1 OFF. NOTIFY("Arrived!", "NOMINAL"). // Turn the cherry light off and tell us we've arrived
}

FUNCTION PARK
{
	STOP().
	// Activate all non-dipole antennas, and deactivate all dipoles
	FOR p IN SHIP:PARTS
	{
		SET mods TO p:MODULES.
		FOR m IN mods
		{
			IF m:NAME = "ModuleRTAntenna" AND p:NAME <> "RTShortAntenna1"
			{
				m:DOEVENT("activate").
			}
			ELSE IF m:NAME = "ModuleRTAntenna" AND p:NAME = "RTShortAntenna1"
			{
				m:DOEVENT("deactivate").
			}
		}
	}
}

 

Link to comment
Share on other sites

First off, what's the goal of each rover? I understand that they are trying to reach a set location but why? To expand your telecom network? To gather science? To simply do that?

Lastly, oceans are going to be an almost impossible a challenge seeing as the land will merge with it anywhere from a 3° angle to 50°+. So your rover, without prior design will drive itself in the ocean. What I suggest is you calculate how far it is from the KSC to your goal and set your rover to stop at that distance. If it stops short then you drive it manually the short distance.

Link to comment
Share on other sites

10 minutes ago, ZooNamedGames said:

First off, what's the goal of each rover? I understand that they are trying to reach a set location but why? To expand your telecom network? To gather science? To simply do that?

Lastly, oceans are going to be an almost impossible a challenge seeing as the land will merge with it anywhere from a 3° angle to 50°+. So your rover, without prior design will drive itself in the ocean. What I suggest is you calculate how far it is from the KSC to your goal and set your rover to stop at that distance. If it stops short then you drive it manually the short distance.

The goal of the rovers is to be a passive relay for the RemoteTech signal.

Also, hmmm...can I get the biome of a LATLNG()? Maybe I can do that, and if the biome of an option is "Water", it'll skip that option.

Link to comment
Share on other sites

14 minutes ago, MAFman said:

The goal of the rovers is to be a passive relay for the RemoteTech signal.

Also, hmmm...can I get the biome of a LATLNG()? Maybe I can do that, and if the biome of an option is "Water", it'll skip that option.

You'd be better off with KeoSync satellites, easier, faster and greater lines of sight.

By the time it says Biome "Water" it could be too late.

Link to comment
Share on other sites

  • 2 weeks later...

fa5.jpg

// Ground navigation library
import("lib_enum.ks").
import("lib_circle_nav.ks").

function aStarSearch{ // Will include multiple sub-functions
	parameter hdg.	// Runs all at once, returning a path.
	global costS is 10.
	global costD is 14.

	createGrid().
	set startPoint to startPoint().
	set currentPoint to grid[startPoint:index].

	set path to list().
	until reachedGoal(){
		// Set the current point as the parent
		set parentPoint to currentPoint.

		local childPoints is list().
		// Assign eight child points from the existing grid:
		//		1	|		2		|	3
		//		----+---------------+-----
		//		4	|	(parent)	|	5
		//		----+---------------+-----
		//		6	|		7		|	8

		// Calculate the g-, h-, and f-costs for each child
		for c in childPoints
		{
			gCost(c, parentPoint).
			hCost(c).
			fCost(c).
		}
		// Set the point with the lowest f-cost as the parent and add it to the path
		path:add(lowestFCostPoint()).
		set currentPoint to lowestFCostPoint().

		// Rinse and repeat
	}
	return path.
}
function lowestFCostPoint
{
	return . // The point whose fCost is the minimum of all points
}
function createGrid{
	global startPoint is currentPoint().
	global spacing is 25. // 25 meters apart
	local _height is 64. // 64 points vertically
	local _width is 64	// 64 points horizontally
	local start is 0
	local iterationNum_x is 0. local iterationNum_y is 0.
	// Total points: 4,096
	// Total height and width: 1,600 m (approx. 1 mile - total coincidence!)
	// Total grid area: 2,560,000 sq. m. (about 1 square mile)
	set grid to list().
	until iterationNum_y >= _height{
		until iterationNum_x >= _width{ // work on the rows first
			grid:add(point((start * iterationNum_x), (start * iterationNum_y), startPoint)).
			set iterationNum_x to iterationNum + 1. // Start a new column
		}
		grid:add(point((start * iterationNum_x), (start * iterationNum_y), startPoint)).
		set iterationNum_y to iterationNum_y + 1. // Start a new row
	}
	return grid. // Hopefully kOS has enough memory! (I hope my COMPUTER has enough memory!)
}
function reachedGoal{
	parameter endPoint is endPoint(), currentPoint is currentPoint().
	if currentPoint["fCost"] = 0{
		return true.
	}else{
		return false.
	}
}
function currentPoint{
	// Runs every loop
	local here is ship:geoposition.
	local currentPoint is point(here:lat, here:lng).
	return currentpoint.
}
function endpoint{
	local endPoint is point().
	
	// Figures out which point is on the opposite side of the grid, with the lowest slope of points surrounding it
	// Runs at the same time as startpoint().
	local endPoint["x"] is _width - startPoint["x"].
	local endPoint["y"] is _height - startPoint["y"].
	return endPoint.
}
function fCost{
	parameter point.
	set point["fCost"] to point["gCost"] + point["hCost"].
}
function gCost{
	parameter point, start is startPoint().
	set point["gCost"] to cost(start, point).
}
function hCost{
	parameter point, end is endPoint().
	set point["hCost"] to cost(point, end).
}
function cost{
	parameter p1, p2. // From the point class
	local deltaX is p2["x"] - p1["x"].
	local deltaY is p2["y"] - p1["y"].
	local costX is deltaX * costS.
	local costY is deltaY * costS.
	local isDiagonal is false.
	if (deltaX and deltaY) <> 0 set isDiagonal to true.
	// If the point is directly diagonal, draw a diagonal line between the points
	// and return the length of that line times costD.
	local deltaDiag is round(sqrt(deltaX^2 + deltaY^2)).
	local costDiag is deltaDiag * costD.
	if isDiagonal{
		return costDiag.
	}	
	// Otherwise, return the sum of costX and costY
	else return (costX + costY).
}
function point{
	// Poor-man's class
	// Points are initialized as offset relative to the point (0,0) on the planet
	parameter dX is 0, dY is 0, reference is latlng(0,0).
	local _x is (360 * dX) / (body:radius * 2 * constant:pi).
	local _y is (360 * dY) / (body:radius * 2 * constant:pi).
	local location is latlng(_x + reference:lat, _y + reference:lng). // *shrug*
	local _z is location:terrainheight.
	return lex("x", _x, "y", _y, "z", _z, "gCost", 0, "hCost", 0, "fCost", 0).
}

3 minutes ago, MAFman said:

fa5.jpg


// Ground navigation library
import("lib_enum.ks").
import("lib_circle_nav.ks").

function aStarSearch{ // Will include multiple sub-functions
	parameter hdg.	// Runs all at once, returning a path.
	global costS is 10.
	global costD is 14.

	createGrid().
	set startPoint to startPoint().
	set currentPoint to grid[startPoint:index].

	set path to list().
	until reachedGoal(){
		// Set the current point as the parent
		set parentPoint to currentPoint.

		local childPoints is list().
		// Assign eight child points from the existing grid:
		//		1	|		2		|	3
		//		----+---------------+-----
		//		4	|	(parent)	|	5
		//		----+---------------+-----
		//		6	|		7		|	8

		// Calculate the g-, h-, and f-costs for each child
		for c in childPoints
		{
			gCost(c, parentPoint).
			hCost(c).
			fCost(c).
		}
		// Set the point with the lowest f-cost as the parent and add it to the path
		path:add(lowestFCostPoint()).
		set currentPoint to lowestFCostPoint().

		// Rinse and repeat
	}
	return path.
}
function lowestFCostPoint
{
	return . // The point whose fCost is the minimum of all points
}
function createGrid{
	global startPoint is currentPoint().
	global spacing is 25. // 25 meters apart
	local _height is 64. // 64 points vertically
	local _width is 64	// 64 points horizontally
	local start is 0
	local iterationNum_x is 0. local iterationNum_y is 0.
	// Total points: 4,096
	// Total height and width: 1,600 m (approx. 1 mile - total coincidence!)
	// Total grid area: 2,560,000 sq. m. (about 1 square mile)
	set grid to list().
	until iterationNum_y >= _height{
		until iterationNum_x >= _width{ // work on the rows first
			grid:add(point((start * iterationNum_x), (start * iterationNum_y), startPoint)).
			set iterationNum_x to iterationNum + 1. // Start a new column
		}
		grid:add(point((start * iterationNum_x), (start * iterationNum_y), startPoint)).
		set iterationNum_y to iterationNum_y + 1. // Start a new row
	}
	return grid. // Hopefully kOS has enough memory! (I hope my COMPUTER has enough memory!)
}
function reachedGoal{
	parameter endPoint is endPoint(), currentPoint is currentPoint().
	if currentPoint["fCost"] = 0{
		return true.
	}else{
		return false.
	}
}
function currentPoint{
	// Runs every loop
	local here is ship:geoposition.
	local currentPoint is point(here:lat, here:lng).
	return currentpoint.
}
function endpoint{
	local endPoint is point().
	
	// Figures out which point is on the opposite side of the grid, with the lowest slope of points surrounding it
	// Runs at the same time as startpoint().
	local endPoint["x"] is _width - startPoint["x"].
	local endPoint["y"] is _height - startPoint["y"].
	return endPoint.
}
function fCost{
	parameter point.
	set point["fCost"] to point["gCost"] + point["hCost"].
}
function gCost{
	parameter point, start is startPoint().
	set point["gCost"] to cost(start, point).
}
function hCost{
	parameter point, end is endPoint().
	set point["hCost"] to cost(point, end).
}
function cost{
	parameter p1, p2. // From the point class
	local deltaX is p2["x"] - p1["x"].
	local deltaY is p2["y"] - p1["y"].
	local costX is deltaX * costS.
	local costY is deltaY * costS.
	local isDiagonal is false.
	if (deltaX and deltaY) <> 0 set isDiagonal to true.
	// If the point is directly diagonal, draw a diagonal line between the points
	// and return the length of that line times costD.
	local deltaDiag is round(sqrt(deltaX^2 + deltaY^2)).
	local costDiag is deltaDiag * costD.
	if isDiagonal{
		return costDiag.
	}	
	// Otherwise, return the sum of costX and costY
	else return (costX + costY).
}
function point{
	// Poor-man's class
	// Points are initialized as offset relative to the point (0,0) on the planet
	parameter dX is 0, dY is 0, reference is latlng(0,0).
	local _x is (360 * dX) / (body:radius * 2 * constant:pi).
	local _y is (360 * dY) / (body:radius * 2 * constant:pi).
	local location is latlng(_x + reference:lat, _y + reference:lng). // *shrug*
	local _z is location:terrainheight.
	return lex("x", _x, "y", _y, "z", _z, "gCost", 0, "hCost", 0, "fCost", 0).
}

 

I have no idea what I'm doing...

Edited by MAFman
Link to comment
Share on other sites

Maybe you can alter an existing auto-pilot for rover use (with permission of the authors, of course)? Such an auto-pilot gives you a framework to work on, for example in regards of heading and throttle control. As far as slopes and biomes are regarded, isn't it possible to use map info from Scansat for that? That also gives you a reason to map a planet extensively :)

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...