Linking Lua with XML, implimenting Classes, and jump tables

Thalassicus,

Would you be willing to create a brief tutorial on creating a custom notification? I'm looking over the custom notification in emigration mod but am having a hard time making heads or tails of it.
 
If you're wondering about the NotificationPanel lua and xml files, I actually didn't do any work on those, so it'd be better to ask killmeplease. I just worked on the emigration.lua and emigration.xml files.
 
It took some doing, but I cracked it. I've since implemented a new CustomNotificationPanel that should make it much easier for people to add their own custom notifications.

You can find it here: CustomNotificationPanel

I'm currently using it in my Building Resources mod.

Thanks again to killmeplease for figuring it out in the first place.
 
Thalassicus,

Would you be willing to explain your use of jump tables? I don't entirely get it.
 
The concept is to take something like this:

Code:
If (functionA) then
   stuff
else if (functionB) then
   stuff
else if (functionC) then
   stuff
else if (functionD) then
   stuff
...etc...
...and replace the long if-then chains with a table:

Code:
functionTable = {
functionA
functionB
functionC
functionD
...
}
Then you can "jump" to functions by index, and dramatically simplify the code:

Code:
If functionTable[i] then
   stuff
This is basically turning an If-Then or Select-Case chain into a function array. The array can be stored in a location outside the code (such as XML), allowing easy changes to how the program operates without code alterations or recompiling.

There's also uses for two-dimensional function tables, especially for programs that have states. A good example is a vending machine.

attachment.php


The program stores the state and input as integers, then simply executes function[state][input] from the table to figure out what to do next.

I used this concept to greatly simplify the checks for the Emigration mod. Basically it looks like this when put in table form:

attachment.php


I simply loop through all the conditions, which are Lua functions, and if (condition) then (multiply by weight value). IsCityStatus indicates the row is a city status to check (to distinguish those from things like the weight for EmigratedOnceAlready).

PHP:
for row in GameInfo.EmigrationWeights() do
  if row.IsCityStatus and city[row.Type](city) then
    cityWeight[cityID] = cityWeight[cityID] * row.Value;
  end;
end

On a side note, the other part of the XML file is configuration settings. In real world programming, it's good practice to separate some types of information away from the core code, at least by putting all constants at the top of the program. Exporting these constants to a file allows you to manipulate the code without recompiling or retesting, and reduces the chance of introducing coding errors when changing data values. This is why Civilization has lots of data stored in XML, and why I exported the constants used in Emigration.lua out to Emigration.xml. These are seen at the top of the file:

Code:
<Emigration>
   <Row>
      <Type>Debug</Type>
      <Value>false</Value>
   </Row>
   <Row>
      <Type>HappinessAverageTurns</Type>
      <Value>10</Value>
   </Row>
   <Row>
      <Type>MaxPasses</Type>
      <Value>3</Value>
   </Row>
   <Row>
      <Type>NumEmigrantsDenominator</Type>
      <Value>10</Value>
   </Row>
</Emigration>

In Windows this type of file is often called an ini file, and in Java it's called a property file.
 

Attachments

  • State Jump Table.JPG
    State Jump Table.JPG
    48.1 KB · Views: 243
  • State Jump Table 2.JPG
    State Jump Table 2.JPG
    33.8 KB · Views: 262
Keeping your constants in the xml... check.
Using coordinates to select a function from a matrix... check.
Keeping those function names in the xml... okay, just like the constants, check.

for row in GameInfo.EmigrationWeights() do... I almost get it.

I'm just having a hard time understanding the real advantage. Is this a performance enhancement or might there be good reason to swap the jump table xml with new functions? Is this mostly useful for testing without recompiling?

How do I know when a jump table is called for?
 
If I could give a simple answer I'd revolutionize software engineering. :)

On a fundamental level, if you have a list of things to do that are all very similar, it makes sense to put it in an array and loop through the list. This is why I basically made a function array for this purpose. If I was only going to be calling 2 functions, it wouldn't make sense to use this method... just have 2 separate if statements. The situation of having a half dozen functions makes the array approach worthwhile.

It's also a matter of industry standards and common practice when it comes to encapsulation/cohesion. The goal is to keep everything compartmentalized, with each component simple. In this case the two components are algorithm and data. I know it's not really necessary in a tiny chunk of code a few hundred lines long like most Civ mods, but I try to maintain good coding practices even for hobbies.

There's basically three variables for a program:
  • Processing efficiency
  • Storage efficiency
  • Time spent coding
As computers become more powerful, the first two are becoming less important. The biggest cost is starting to be time, so easily readable and maintainable code is often better than efficient code. A table with conditions and actions, then a simple "for each condition do action" loop is easier to maintain. If you want to add or remove functions, you can just do it in the XML table, the algorithm is untouched. Or it could be a table created in the lua code itself, the concept is the same. This keeps everything encapsulated in small and highly cohesive elements (algorithm is here, data it operates on is there).

Bigger programs operate on the same principle. All the data is stored in a database somewhere on a server cluster. The program is separate, and operates on that data.
 
Okay, I think I understand now. I appreciate you taking the time to explain it. I'll study your code in Emigration mod a bit more and see if I can't find applications for this approach in my own coding later. I'm just not certain anything I'm creating is large enough in scope for it to apply.
 
The thought occurred to me that a matrix of function pointers could prove useful in coding an AI that learns from experience. Wow... just think of it, as the AI reprograms itself based on game results, the new logic structure could be saved and reused the next time, each iteration making it smarter and distributed via XML updates! Civ's Deep Blue?

You know, if done properly, it could possibly make late game turn length shorter rather than longer, based on what I've read regarding inefficient unit movements making up for for lack luster defensive lines. Just an example, but a smarter AI could actually be a time saver, not a time killer, and jump lists would prove instrumental I think.
 
It's a similar concept to something called clustering I've been involved in lately with a research program on campus. Basically each AI has functions it performs, and looks at 'neighboring' AIs to decide how to modify its actions. The data is often structured in a graph format, and stored as a matrix.

We've been using this to improve automated bug-finding for programmers. Each AI considers locations it suspects of containing bugs in programs, and collaborates on this information with fellow AIs.

However, if a Civ V AI ever asked us if we want to play a game of chess, I think we should pull out a crowbar and bash our computer! :lol:
 
I've never worked on AI, but it's certainty interesting. I was reading a xkcd comic about Lisp when I realized any language with function pointers could use a jump list for AI.

Your example of compartmentalization is intriguing. Do you think it's beyond the scope of Civ5?

Ideally, the AI should have quick turns based on logic learned from previous games, rather than churn endlessly on every turn possibility. Perhaps the AI could have 2 modes: practice and competition. Practice mode would involve a lot of churning with the full AI learning as much as possible with each move. Competition mode would be a streamlined version of the best logic learned with far less second guessing of moves. Thus we could largely separate the most CPU intensive operations away from actual competitive game play.

Oh, and you can forget about the crowbar. The moment an AI achieves self awareness... Well, I know a winning side when I see one. ;)
 
It's beyond the scope of what I can do, at least. If this sort of learning AI were possible for games it would've been done already by teams with way more time and budget than I. :)
 
Of course it's possible. That's not really in question. But man... just thinking of the development time makes me grow older.

I like the notion of using bitwise values and operators for the coordinate system. Could do all sorts of interesting implementations with that.

All of it a bit over my head tho. :)
 
Oh it's just a matter of scope. No one says it has to beat Kasparov or fill in for Central Command. I'm thinking an AI mini, applied to specific concepts, like plot improvement and citizen allocation, not the entire game strategy. Could be really useful for teaching the computer player how to use modded plot improvements, for example. I mean it wouldn't be perfect of course, as in this example each city would follow a self-centered path of progress, without taking the rest of the empire into account. But then, I'm pretty sure that's how the current city AI works. :p

edit: ...and the only time constraint is the length of time this game remains in play. There is no budget constraint, as their is no budget. Just time spent learning the in's-and-out's of AI design. :)
 
Back
Top Bottom