Author Topic: OP2 Scenario Project for C#  (Read 20078 times)

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 908
Re: OP2 Scenario Project for C#
« Reply #25 on: March 19, 2019, 10:10:25 PM »
We have some minor changes brewing in Outpost2DLL, OP2Helper, and HFL. Would be nice if we could get them into OP2MissionSDK since it sounds like TechCor is actively using it.

Nice to see some more C# development going on. Make me feel like a bit of hack with how quickly this is moving compared to the C++ code I am slogging through.

I enjoyed seeing the auto base builder results.

-Brett

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4811
Re: OP2 Scenario Project for C#
« Reply #26 on: March 20, 2019, 02:00:59 AM »
Thanks for the update!

Now it runs package restore, though fails at a later step (edited paths):
Code: [Select]
/bin/sh: 2: /tmp/tmpcac72cbbf94540508eaf575e65118431.exec.cmd: copy: not found
/.../OP2DotNetMissionSDK/DotNetMissionSDK/DotNetMissionSDK.csproj(8,5): error MSB3073: The command "copy "/.../OP2DotNetMissionSDK/DotNetMissionSDK/bin/Debug/netstandard2.0/DotNetMissionSDK.dll" "*Undefined*Outpost2\DotNetMissionSDK.dll"" exited with code 127.

Looks like you have a shell command as part of the Post Build step. Shell commands can easily lead to a few incompatibilities. Here, Linux uses "cp" rather than "copy". There also seems to be a strange "*Undefined*" in the path, which looks like use of an environment variable.

I checked DotNetMissionSDK.csproj and saw this:
Code: [Select]
  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="copy &quot;$(TargetPath)&quot; &quot;$(SolutionDir)Outpost2\$
(TargetFilename)&quot;" />
  </Target>

It seems $(SolutionDir) was undefined. That makes sense, since I tried to run the build from the "DotNetMissionSDK/" folder to avoid errors with the other projects in the solution file.

Doesn't look like there is much in that project file in terms of build settings, or even references to source files. I'm guessing C# is a little smarter about finding the source files for a project.



The following might help for fixing the Post Build command:
Best way to make a project with post-build script work on MonoDevelop and Visual Studio?
The MSBuild Task answer looked promising.
« Last Edit: March 20, 2019, 02:02:41 AM by Hooman »

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #27 on: March 20, 2019, 02:48:02 AM »
Nice! I didn't know it could be done that way!

I replaced line 8 in DotNetMissionSDK.csproj with:

Code: [Select]
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(SolutionDir)Outpost2" />
It seems to work. You will need to run from the solution however.

This one with the $(ProjectDir) also works and may be the better choice:

Code: [Select]
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(ProjectDir)../Outpost2" />
The C++ post builds did not work however. The project won't load when I do that. I'm sure those projects have a lot more problems than a convenience post-build step though.
« Last Edit: March 20, 2019, 02:50:47 AM by TechCor »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4811
Re: OP2 Scenario Project for C#
« Reply #28 on: March 20, 2019, 03:53:04 AM »
That was quick.


I tried out the first option, and when I built the specific project file, it generated a weird "*Undefined*" directory and copied to that.

I tried out the second option and when I built the specific project file, it worked.


For either option, when I tried to build the whole solution, I see what looks like some partial success, and then a whole bunch of errors from the other projects. The output file was not updated. I don't think the Post Build step ran. Perhaps the Post Build step didn't run due to errors in the other projects. I assume it only runs after a successful compilation.



Edit:
As for C++ projects, there are much deeper problems that prevent them from building on Linux. The whole project file structure ends up getting significantly changed. C++ uses a different compiler on Linux than for Windows, so the references to source files end up in different tags referring to a different compiler. The compiler options are also completely reworked. I've never seen a C++ project file that contained both Windows and Linux settings in the same file. All the examples were either one or the other.

C# seems to be much more standardized in terms of compiler and compiler options across platforms.



Edit 2:
From the solution folder, I tried running:
Code: [Select]
dotnet build DotNetMissionSDK

With the first option, it appears to compile successfully, though the output file is not updated. No strange folders observed.
With the second option, it appears to compile successfully, and the output file is updated as expected.
« Last Edit: March 20, 2019, 04:13:46 AM by Hooman »

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #29 on: March 21, 2019, 03:22:53 PM »
The last thing to do for AutoLayout is get map wrap working, but I'm having a bit of trouble understanding the values I'm getting back for clip.

on6_01.map is a 128x128 map.

When I clip the map, I get an area of (32,0) to 160,127) inclusive as valid tiles.
This comes out to a size of 129x128. That can't be right.

Maybe it would help if I knew why maps can start at arbitrary points.

newworld.map gets an area of (0,0) to (511,255) which comes out to a 512x256 size. Which is fine.

Here's the code I'm using to calculate this:

Code: [Select]
// Calculate map size and determine if map wraps
// Clip to top left
LOCATION mapSize = new LOCATION();
mapSize.ClipToMap();

// Initialize to top left corner
area = new MAP_RECT(mapSize, mapSize);

for (int i=0; i < MaxMapSize+10; ++i)
{
int x = mapSize.x;
int y = mapSize.y;

++mapSize.x;
++mapSize.y;

mapSize.ClipToMap();

// Clamped map will pull mapSize.x back to x.
// The max value is the current and previous value.
if (mapSize.x == x)
{
area.maxX = x;
doesWrap = false;
}

// Unclamped map will wrap mapSize.x back to 0.
// The max value is the previous value.
if (mapSize.x == 0)
{
area.maxX = x;
doesWrap = true;
}

// Y is always clamped.
// When mapSize.y is pulled back to y, and we have our clamped or wrapped values, end search.
if ((mapSize.x == x || doesWrap) && mapSize.y == y)
{
area.maxY = y;
break;
}
}

EDIT:

Since the area on the wrap map is correct. I was able to get base generation working there. I tested generating the base near the edges of the clamped map to see if it also worked, and it does.

I'm going to assume that 129 width is expected unless someone says otherwise.
« Last Edit: March 21, 2019, 04:40:43 PM by TechCor »

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 908
Re: OP2 Scenario Project for C#
« Reply #30 on: March 21, 2019, 10:26:19 PM »
Map starting position has two options. Nonwrap maps have offsets of (31,-1). Typically I use the constants X_ and Y_ defined in op2helper.h when making maps.

Check the usage note on cliprect in map.h from the project op2utility for a bit more detail. https://github.com/OutpostUniverse/OP2Utility/blob/master/src/Map/Map.h

Hooman could maybe give you a bit more technical details about it. Also in the sun repo there is a ollydbg section that contains detailed notes about different files, structures, and code related to Outpost 2. Maybe a good reference to help with understanding.

-Brett

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #31 on: March 22, 2019, 12:18:30 AM »
That's good to know that those offsets don't change arbitrarily from map to map.

I'm thinking I could clip -1,-1 to determine if the map wraps and hard code the lower bounds. Then use the result for wrap upper bounds, or clip again at 5000,5000 for the upper bounds for clamped maps. (The for loop used above is a bit inefficient. ;) )

The question is why am I getting the values I am from clip, as I'm using that function everywhere to resolve clamp/wrap. It seems I can't trust the values coming from it.
« Last Edit: March 22, 2019, 12:25:20 AM by TechCor »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4811
Re: OP2 Scenario Project for C#
« Reply #32 on: March 22, 2019, 06:07:18 AM »
For non-around-the-world maps, there is an internal memory offset of one 32-column wide section of tiles for the map data. As the internal map coordinates are 0-based, but the displayed values are 1-based, they also need to be offset by 1. Hence you end up with display coordinates to internal coordinates being offset by (x + 31, y - 1).

I assume the offset is to add padding to the map data, so when units drive off the edge of the map, it doesn't crash the game. I don't think they did proper clipping and bounds checking, so that was probably a hack to prevent crashes. In terms of memory allocation, non-around-the-world maps actually allocate twice as much memory as necessary. That gives them room to have padding on both sides of the map, with possibly a lot of extra padding on the right side.

For around the world maps, the coordinates are always wrapped, so they'll never be out of bounds. Hence they have no need for that internal offset.

You can find some details in:
FileFormats: Read down to post about the Saved Game File Format. In particular, the ClipRegion struct (or ClipRect in some sources).

The around-the-world aspect is controlled by the map width. From the above, if lgMapWidth >= 9 (or equivalently, if mapWidth >= 512) then it's an around-the-world map.

Additionally, the bottom edge of the map is marked as impassible. I haven't verified if this is all maps, or only around-the-world maps, or just some maps. I assume that's probably also to deal with clipping problems. Hence the ClipRect may exclude the last row of tiles.



Edit: The 129 is probably not correct.
(160, 127) - (32, 0) = (128, 127)
Those values should be the size, which for 0-based indexing would mark a non-inclusive endpoint. Valid values are (0..127, 0..126) inclusive.
« Last Edit: March 22, 2019, 06:16:21 AM by Hooman »

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #33 on: March 22, 2019, 11:06:45 PM »
You said "around the world" so many times I have ATC in my head.

So Clip() doesn't clamp to the playable area, but includes the max exclusive value for the x-axis unless the map wraps. I guess that's a bug? I changed everything to use the correct inclusive maximum. C# now uses its own clip instead of Outpost2DLL's.

When I saw the 32 value initially, I thought it was some cool map cropping feature that some games use to only show a portion of the map. Often completing objectives would "unlock" more playable area. What a let down.


I'm done with AutoLayout for now. The only thing that bothers me is the tube aesthetic isn't perfect, but it would be quite complex to cover all the cases to make it look nice. Might come back to it later.

Thanks for the help with tile offsets, guys. Here is your reward:  8)

(AutoLayout at map edge)

Going to start making general-purpose AI.

I'm a little concerned about the limited API functionality.

It doesn't appear possible to get a structure's demand through Outpost2DLL or HFL, so I may just wing it with like pop / 25, but there could be other problems that can't be resolved. Time to see how well this goes.
« Last Edit: March 22, 2019, 11:08:57 PM by TechCor »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4811
Re: OP2 Scenario Project for C#
« Reply #34 on: March 24, 2019, 04:25:24 AM »
Lol @ATC  :P



Normally, when doing bounds checking, I generally expect some kind of Point + Size to determine a Rect, much like how a for loop will loop over arrays:
Code: [Select]
for (int i = 0; i < container.size(); ++i)

It's pretty easy and standard to write 0-based array code that is start point inclusive, and end point exclusive.

When looking at the Rect struct in Outpost 2, I assumed it would be the same. After reading your comment though, I decided to go check. In particular, there is the int Check(LOCATION& ptToCheck); method for hit testing if a point is in a Rect. It turns out to be a little more complicated and bizarre than I thought. There's a bit of code to determine if the Rect wraps around the edge of an around-the-world (ATC) map, but other than that complication it's basically checking: (y1 <= y < y2) and (x1 <= x <= x2). Note the end point conditions. It's exclusive for y, but inclusive for x. I'm thinking that's got to be some kind of bug.



I suppose you could still try modifying the clip rect, and try to unlock an area of the map. If you clip a section of the map, it's likely to still be visible, but the mouse cursor will likely change when you hover over an impassible area of the map. Maybe with a bit of day/night hacking, you could also make such a section of the map permanent night time until it is unlocked. Might not be quite what you were looking for, but perhaps close enough.



As for building requirements, I don't believe we currently have a way to get it, but I could probably find a way to get that info for you. I know where it is in memory, there's just no API written (yet) to access it. Same for the day/night position.



Oh and really cool image btw. Looks like it handles corners and restrictive complicated terrain quite well.  :)

Offline Crow!

  • Jr. Member
  • **
  • Posts: 72
Re: OP2 Scenario Project for C#
« Reply #35 on: March 25, 2019, 11:26:04 AM »
The biggest thing I'm thinking about for how I would actually use this function is that, in most scenarios, there will be one or two structures you want to demand are put in a particular place.  Smelters come to mind here, as well as enemy structures that are mission objectives in some way.  If you use the manual structure placement you mentioned before, will it know to build a tube from the final autolayout-ed base to wherever the manually placed structure is, or does it simply know not to cause unit overlaps with it?
Speedruns, my FFIV game randomizer, and more can be found at my twitch page:
https://twitch.tv/iicrowii

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #36 on: March 26, 2019, 09:14:18 AM »
The biggest thing I'm thinking about for how I would actually use this function is that, in most scenarios, there will be one or two structures you want to demand are put in a particular place.  Smelters come to mind here, as well as enemy structures that are mission objectives in some way.  If you use the manual structure placement you mentioned before, will it know to build a tube from the final autolayout-ed base to wherever the manually placed structure is, or does it simply know not to cause unit overlaps with it?
You make a good point.

I just added a "MaxTubes" attribute to UnitData that defaults to -1 (unlimited). This can be used to control the number of tubes to any structure passed to BaseGenerator. Tokamaks, Light Towers, Mines, etc will default to 0 if the value is -1, so you will be able to connect those by tubes as well, if desired. This attribute applies even with IgnoreLayout set to true. Same with CreateWall.

If all else fails, you can always use the PathFinder to generate a path between structures for creating tubes. It's a little more complicated, but can help create extra tubes if the BaseGenerator isn't putting them where you want them. All generated units are put in a list, so you can scan the list for the auto structure you want to connect.

Quote from: Hooman
It's pretty easy and standard to write 0-based array code that is start point inclusive, and end point exclusive.
[snip]
Saying this made me rethink using max inclusive. I looked at some other frameworks, and apparently you are right, it is standard to use max exclusive. It seems strange to me that a rect contains a point on its min edge, but not its max edge. However, I also think that a rect of zero size shouldn't be able to contain a point, even if it is infinitely small. Anyway, it's been standardized.




Just kind of want to outline how I'm going to set up the AI.

Managers
The AI is going to be set up as three managers. Managers assess the situation and prioritize goals using heuristics and goal weights. Goals are top-level tasks. Managers have no direct dependencies on each other. They assess the situation and perform their own goals to the best of their ability.

The managers are independent to prevent task blocking. For example, if the BaseManager needs a spaceport, but Space Program has not been researched, it will get stuck waiting for the research to complete rather than pursue other goals. CombatManager would block the other two managers if it's performing a raid, and so on.

BaseManager - Handles deployment, morale, and economic tasks. Also builds military.
ResearchManager - Chooses the next research topic for the colony. Base manager assigns the number of scientists to the lab.
CombatManager - Directs combat tactics with the military units available.

Tasks
Tasks represent what an AI must do to complete a goal. They are arranged as a tree. The current goal task is at the top. When a manager wants to achieve a goal, it calls PerformTask on it.

Tasks contain a list of dependent tasks underneath it that are required to complete the current task. Perform task first checks these tasks to see if they are complete (IsTaskComplete()). If all are true, the task performs itself. If any are false, those unfinished tasks get PerformTask called on them. This can continue all the way down to bottom-level tasks that have no dependencies.

Examples of top-level tasks include "LaunchSpaceportEvac", "CreateMilitaryUnits", "HarassEnemy", and "FixDisconnectedStructure".

If, for example, the goal task is "FixDisconnectedStructure", it would check IsTaskComplete on the "CreateEarthworker" task. "CreateEarthworker" would check "CreateVehicleFactory".

Some tasks can't be completed, and the AI needs to give up on the goal to pursue other things. PerformTask returns false if a task cannot be performed.

In the example above, "CreateVehicleFactory" may not be able to complete its task. If there is no structure factory (or convec with it) or Cybernetic Teleoperation is not researched, the task cannot be completed. The manager is informed via the return, and chooses a different goal.

Bot Types
Bot types or "personalities" will be based on goal weights. I intend to support the following predefined types:

Code: [Select]
PopulationGrowth,	// Bot focuses on growing population. Keeps enough defense to avoid being killed. Will build Recreation, DIRT and other optional structures.
LaunchStarship, // Bot focuses on launching starship. Keeps enough defense to avoid being killed.
EconomicGrowth, // Bot focuses on resource acquisition. Keeps enough defense to avoid being killed.
Passive, // Bot does not build new structures. Keeps enough defense to avoid being killed.
Defender, // Bot will build military units and defend itself and allies. Does not attack.
Balanced, // Bot will build military units and defend itself and allies. Attacks with best available strategy.
Aggressive, // Bot will build military units and won't defend itself or allies. Attacks with best available strategy.
Harassment, // Bot will build military units and harass trucks, power plants, and unescorted or poorly defended utility vehicles.
Wreckless, // Bot will build military units and send them to attack even against overwhelming odds.

A predefined type can be selected, and then the weights tweaked to get the desired effect. For example, you can choose "Aggressive" and tweak how much it focuses on population and economic growth to either confine it to its starting base, or have it attempt to build all over the map. You can also change these weights at any time, so a dormant AI can be triggered to suddenly turn Aggressive or Wreckless.



The greatest obstacle is going to be suboptimal access to player state. I'm thinking of creating a PlayerInfo class to start pooling information in an efficient manner at the beginning of each update.

For example, I am working on the LaunchStarshipTask, and I need to know if the evac module has been deployed. I believe I can iterate over the player units to find it, but I need to also check if the phoenix module has been deployed in another task, the orbital module in another, and... well.. you can see where this is going. Hundreds of loops to pull single items from the player state.

There are other things that are harder to get due to limited API - Beacon states (was it surveyed?), structure demand, and possibly many other things.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4811
Re: OP2 Scenario Project for C#
« Reply #37 on: March 26, 2019, 10:16:51 AM »
Very neat. I'd started on a task and dependency management scheme years ago, but the efforts stalled out after I spent all my time tracing dependencies down rabbit holes. You can really take that stuff too far.

For instance, if any any stage, you don't have the immediate resources to do something, trace down to the way to get that resource:

Build structure Type at location (x, y) Depends on Moving ConVec with kit Type to (x, y)
Moving ConVec with Kit at (x, y) depends on having ConVec with kit type, (and (x, y) being move accessible)
Having ConVec with kit Type depends on Loading ConVec with kit Type at Structure Factory
Loading ConVec with kit Type at Structure Factory depends on having a ConVec docked at Structure Factory, and Structure Factory having kit Type in storage, and Structure Factory being enabled.
Having a ConVec docked at a Structure Factory depends on Moving a (free?) ConVec to dock location
Having kit Type at Structure Factory in storage depends on completing research to build kit Type, and having resources (common, rare) needed to build it, and on Structure Factory being enabled.
...
Moving a (free?) ConVec depends on having a (free?) ConVec
...
Having a ConVec depends on Having a Vehicle Factory, having completed research for building ConVecs, having the resources (common, rare) to build a ConVec, and the Vehicle Factory being enabled
...
Having a Vehicle Factory depends on building a Vehicle Factory ... (loop back to top)
...
Having resources (common, rare) depends on being able mine resources (common, rare)
Mining resources (common, rare) depends on ...


You get where that went. The resources were particular messy, as they effectively depended on themselves. If you didn't have a cargo truck, or smelter, or mine, or robo surveyor, you needed to build these things, and that depended on having resources (common, rare). Potentially things could deadlock with unsatisfiable requirements.

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #38 on: March 26, 2019, 12:26:32 PM »
Technically a player could receive an impossible task, too. No cargo trucks or metal and you're done.

Clearly if you have no convecs and no vehicle factory, you can't create a vehicle factory. Hence the need to declare the task impossible to complete. The question is at what point is it impossible.

Create Military Unit >
Need Vehicle Factory? Yes >
Need Structure Factory? Yes >
Need Convec with Structure Factory? Yes >
Fail out / Task impossible
...
Need Structure Factory? No >
Need free convec? Yes >
Fail out / Task impossible

But what if you have a vehicle factory? Well.. then you wouldn't need to go all the way down the tree to create a military unit.

Option 1:

Each goal can have its own set of tasks. Therefore, no circular dependencies. Downside? Massive code redundancy.

Option 2:

Each task tracks its parent. When you check if the task is possible, check if any prerequisites is an ancestor up the tree.

Create Military Unit >
Need Vehicle Factory? Yes >
Need Structure Factory? No >
Need free convec? Yes >
Is the VehicleFactoryTask prerequisite an ancestor of this task? Yes >
Fail out

Pseudo code:

Code: [Select]
CreateConvecTask : Task
{
    VehicleFactoryTask m_Prerequisite;
    Task m_Parent;

    if (!m_Prerequisite.IsTaskComplete())
    {
        if (IsAncestorTask(m_Prerequisite))
            return false; // Fail out
    }

    bool IsAncestorTask(prerequisite)
    {
        if (m_Parent == null)
            return false;

        if (prerequisite.GetType() == m_Parent.GetType())
            return true;

        return m_Parent.IsAncestorTask(prerequisite);
    }
}

I think that would terminate all circular tasks. Let me know if you think otherwise.

EDIT:

Here's Option 2 integrated into the current Task class.
Code: [Select]
public abstract class Task
{
private Task m_Parent;

protected Player m_Owner;
protected Task[] m_Prerequisites = new Task[0];


public Task(Player owner, Task parent)
{
m_Owner = owner;
m_Parent = parent;
}

public abstract bool IsTaskComplete();

public virtual bool PerformTask()
{
if (!PerformPrerequisites())
return false;

return true;
}

private bool PerformPrerequisites()
{
for (int i=0; i < m_Prerequisites.Length; ++i)
{
/* Skip completed tasks */
if (m_Prerequisites[i].IsTaskComplete())
continue;

/* If a prerequisite is an ancestor task, then we have a circular dependency. This task cannot be performed. */
if (IsAncestorTask(m_Prerequisites[i]))
return false;

/* Perform the task. Fail out if it can't be done. */
if (!m_Prerequisites[i].PerformTask())
return false;
}

return true;
}

private bool IsAncestorTask(Task task)
{
if (m_Parent == null)
return false;

if (task.GetType() == m_Parent.GetType())
return true;

return m_Parent.IsAncestorTask(task);
}
}

...And then presumably the manager picks a new top-level task if it fails.

I admit, the sheer amount of tasks that have to be considered is a PITA, but I don't think the architecture is flawed in itself.
« Last Edit: March 26, 2019, 01:56:11 PM by TechCor »

Offline lordpalandus

  • Hero Member
  • *****
  • Posts: 780
Re: OP2 Scenario Project for C#
« Reply #39 on: March 26, 2019, 05:43:14 PM »
For my game I'm developing, I like to use something obvious to quickly cancel an activity, so that processing through various steps is quick, when failure is obvious. So for my game, instead of going through all of the code to setup variables for a technique, I first check if the player has enough energy to cast that technique in the first place. Energy is a single variable, so it is quick to check and if you lack energy, then it is quick failure state.

What I mean by this is:

In the case of construction of a military unit, the first thing that should be checked is cost. It is quick to check cost, as they are likely a globally accessible variable. If you lack the resources, there is no point checking all of the other steps.

Then, if you have the resources, the next step to check would be research. Why? Because I'd expect that all structures would be contained within a vector, and thus you'd have to search the entire vector to see if you have an active vehicle factory (as an inactive one would prevent them from building anything). If they lack the research to construct a unit, that then would be a quick check as well (even if research was stored in a vector, you'd likely be looking for a Boolean True for a researched topic)

Then check for an active vehicle factory. Then check to see if the vehicle factory isn't producing anything, and then build said unit.
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #40 on: March 26, 2019, 10:16:15 PM »
OK. Based on feedback, I've made a dependency graph for spaceship modules. The idea is to limit the decision tree so it doesn't sprawl and become unmanageable.


NOTE: "IsEnabled" should be a CanPerform requirement for structures.

The top-level task order is not quite right. BaseManager should be performing a weighted situational assessment to determine priority in addition to whether or not the task can be performed.

So for example:

Choose DeployEvacModule, but if I don't have a smelter and trucks, prioritize that.
If common metal is below X amount for Y duration, prioritize CommonMetalExpansion instead.
If enemy is stronger than me, prioritize defense units over the spaceship.
If I have no structures at all, deploy a base.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4811
Re: OP2 Scenario Project for C#
« Reply #41 on: March 27, 2019, 01:02:11 PM »
@lordpalandus: In terms of checking resources and failing early, that doesn't quite work with dependency management when you are trying to optimize time to goal completion. As a task may require many lengthy to perform dependencies, such as research, you shouldn't fail out on moving towards a goal because you don't currently have the metals to produce it. You still need to check what those lengthy dependencies are, such as doing research, or setting up mining operations, and if the prerequisites are satisfiable. It may also be that some prerequiste tasks can be done in parallel. For example, you can setup mining operations while doing research.



@TechCor: Will that algorithm work? I don't really know. I'm not sure I quite understand it yet.

As for the graph, I should point out land rush games. At the start, you don't have a Structure Factory, nor do you (usually) have empty ConVecs, but you do usually have structure kits loaded in ConVecs. As such, I think the graph needs an extra node for all structures, which is the single prerequisite of having a ConVec loaded with the proper kit. At that node, you would then fan out to having a Structure Factory, and having a ConVec. I suppose the ConVec need not be empty, since you would swap kits at the Structure Factory when loading a new kit, though generally speaking, if the ConVec wasn't empty, it was probably already given a different task.

I noticed you modelled that dependency for the Structure Factory, but not the Spaceport.

As for balancing priorities, that's a whole other ballpark. I don't really have a clear idea how to handle that.

I suppose if you get the dependency management right, you do get some minimal inbuilt priority system. Such as setting up mining to build the components for the spaceship. Such dependency tracking wouldn't be enough to shift resources between launching an evacuation module, or building an army for defense though. Or shifting research between starship components and weapons.



If you really want to complicate things, there are also maps that can get reinforcements from off the map. Perhaps a ConVec shows up with a needed kit at some delayed time. Though that's a complication that can probably be ignored starting out. There's no real way to know those sorts of things without advance knowledge. Would be nice to have custom pluggable inputs to let the algorithm know about such events though.



Looking more closely at the dependency loop detection, I'm thinking it may be more complicated. The cycle can close at any point, so you don't know where the head of the loop is. It might be 5 levels down before the start of the loop, and maybe another 10 levels until it closes. Those tasks may have seemingly very little to do with the target goal.

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #42 on: March 27, 2019, 06:24:17 PM »
I think you are looking at the tasks too granularly.

Let's look at how HaveSpaceport would work if we don't have a spaceport.

We start in HaveRocket and ask, HaveSpaceport.IsTaskComplete?

TaskComplete If:
Spaceport exists
Spaceport has empty bays (This should basically be always true)

Task is not complete, so we attempt to perform the task.

Prerequisites:
HaveStructureFactory
HaveConvec

If we don't meet prerequisites, perform those tasks first. If they fail, task fails, manager can decide what to do, if anything.

Now, we assume we meet all the criteria above and the task isn't complete, perform task:

If no CC, return FAIL (If a convec has a CC, the manager should be prioritizing SetupInitialBase task)
If spaceport inactive or idle, return FAIL (Fixing this issue is delegated to another task, BaseManager for tubes, EmploymentManager for worker/scientist distribution)

If spaceport is under construction, return
If spaceport in convec and convec is moving, return
If spaceport in convec and convec is NOT moving, build spaceport, return
If spaceport in structure factory and convec is empty and on dock, transfer cargo, return
If spaceport in structure factory and convec is empty and not on dock, move to dock, return
if spaceport in structure factory and convec is NOT empty and is moving, return (Wait for operation to complete)
if spaceport in structure factory and convec is NOT empty and is on dock, transfer cargo for spaceport
if spaceport in structure factory and convec is NOT empty and is not moving and not on dock, move to dock
If metals < cost) return FAIL (Wait for metal, or expand mining operations, decision to be made by manager)
If spaceport not in structure factory, build spaceport kit, return


In real life, tasks are delegated to the appropriate teams by a manager. Let's look at decision making:

Manager: "Deploy the satellite!"
Engineer: "Well, I need a spaceport, and I can build one for you, but I don't have the materials."
Manager: "Damn it, Bobby. Hurry up, blue collar guys!"
...
Manager: "Ok, you guys are taking too long gathering those metals, forget the satellite for now, ExpandCommonMetalSupply when you save up enough.
...
Manager: "Our tokamak just exploded! Change task to MeetPowerDemand! Priority one! If it fails, just wait for resources!

etc.

Managers decide what to do. The task tree is how to accomplish what the manager wants. If what the manager wants can't be done / is outside your field, tell him so he can update priorities as necessary.
« Last Edit: March 27, 2019, 06:44:34 PM by TechCor »

Offline lordpalandus

  • Hero Member
  • *****
  • Posts: 780
Re: OP2 Scenario Project for C#
« Reply #43 on: March 27, 2019, 11:38:34 PM »
AI is computationally expensive. If you can provide a fail condition early in the AI tree, it will save you computational resources for other things. You could provide an event after failing to check back on the task in one minute (or 1800 frames -> 30FPS * 60SEC), and see if have resources then. Or fail out of the construction task, and create a new task to gather the resources necessary and then retry the original task when you have required resources.

AI for strategy games can get quite expensive fast, so creating ways of optimizing it and exiting out of a lengthy AI tree, when failure is ensured, will really help with that. If it cannot perform a task, there is no point in checking the other dependencies of the task.

Or if you want the AI to perform several mutually exclusive tasks at once (ie mine resources and research), you could provide several failure conditions to check and if any failed, it aborts the task and looks to turn the failed conditions into successful ones and then retry the task the moment all conditions read as successful. Think of trying to solve all failure conditions as a quest in an rpg; it is successful when you've "gathered" all the necessary components.

Ex.

If you want to build say a Vehicle Factory, it would have these mutually exclusive conditionals:
-> Have available adults
-> Have power
-> Have convec with kit
-> Have technology

If any of these fail, it aborts/postpones the task to construct the factory. Say, it fails on Adults, Power, Convec with Kit. So it generates three new tasks: Grow Worker Population, Construct Power Plant, and Build Kit. Once population reaches necessary amount, Adult task is complete. And so one... until all conditions are satisfied.

Then it produces a task to place the kit in a specific area. While it is being built, it checks to make sure that there is a tube connection, and if not, build one. If the building is built, and a power plant was destroyed, it will idle the structure. Etc...

Ughh... this got complicated fast. I'm going back to developing my roguelike... so much simpler.
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2208
    • LairWorks Entertainment
Re: OP2 Scenario Project for C#
« Reply #44 on: March 28, 2019, 06:44:27 PM »
AI doesn't have to be expensive -- in terms of games it's not a true AI, it's a fake AI. Have it run tasks in a separate thread maybe once every three to five frames and it need not be expensive. Many commercially successful games do this including World of Warcraft, StarCraft and Age of Empires (you can actually see the lag in AoE2 -- select a unit and direct it to move somewhere, you'll see it hesitate for a few frames before it actually starts to move). Quake III Arena does the same thing with its bots.

Offline lordpalandus

  • Hero Member
  • *****
  • Posts: 780
Re: OP2 Scenario Project for C#
« Reply #45 on: March 29, 2019, 12:45:41 AM »
Most things that get called AI are not true AI. No videogame in existence has a true AI... that would be way too computationally expensive to have. So, we fake it with something that appearances to have intelligence with decision trees, probabilities, build orders, etc... to create an illusion of an intelligent AI. Some games does it better than others; ie First Encounter Assault Recon (FEAR) had exceptionally smart "AIs" and as far as adaptability goes for multiplayer bots, Unreal Tournament still is the best with them.

The closest thing the world has seen of a true AI would be that "social experiment" back in 2017? with TayAI, which I think was a learning program based on a neural network fed information from the interactions the AI had with people and adapted their personality and knowledge-base based on the information provided to TayAI.

The world isn't ready for a true AI. If we cannot get along with eachother, avoiding wars, hatred and racism, then we stand no chance of coexisting with an AI. More likely the AI would take over, with either a "Matrix scenario" or a "Skynet scenario".
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2208
    • LairWorks Entertainment
Re: OP2 Scenario Project for C#
« Reply #46 on: March 29, 2019, 03:43:14 AM »
Thread is starting to derail. Philosophical discussion about what a true AI is and whether or not humanity is ready for it can be started in a new topic.

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 908
Re: OP2 Scenario Project for C#
« Reply #47 on: March 29, 2019, 07:46:20 PM »
TechCor, there is a new PR in OP2MissionSDK that pulls in new library changes. Wanted to bring it to your attention before merging since I think you are actively using the library. Unless you are using deprecated portions, I do not anticipate any issues (famous last words). Also opened a new forum thread if you want to post any concerns, additions, etc.

Thanks,
Brett

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #48 on: March 30, 2019, 01:31:41 AM »
TechCor, there is a new PR in OP2MissionSDK that pulls in new library changes. Wanted to bring it to your attention before merging since I think you are actively using the library. Unless you are using deprecated portions, I do not anticipate any issues (famous last words). Also opened a new forum thread if you want to post any concerns, additions, etc.

Thanks,
Brett
Sounds good. I'm watching the other thread and will get the changes when they are ready.


HFL is now in the C# project (with the exception of panes).

Starting AI now, we'll see it how it goes.  ;)

Updated OP to reflect the current status of the project - what's missing, what's new.
« Last Edit: March 30, 2019, 05:11:04 AM by TechCor »

Offline TechCor

  • Jr. Member
  • **
  • Posts: 69
Re: OP2 Scenario Project for C#
« Reply #49 on: March 31, 2019, 04:58:46 AM »
Does anyone know how to transfer cargo to the spaceport rocket?

I can't seem to figure this one out. I tried DoTransferCargo, but I'm pretty sure that's for the dock.

Scanned types in Unit/Building enumerators and couldn't find an SULV.

--
Can't seem to get the status of a structure either. Kind of necessary to redirect to another task if disabled.
« Last Edit: March 31, 2019, 05:31:53 AM by TechCor »