Load AI from file?

I am making a two player strategy game and I have the local multiplayer ready. Now for single player mode, I want to implement an AI that gets the state of the game (I already have a variable for that), and then the AI should refer a file that tells it which move to play in that state.

Is it possible to do something like this, maybe with a xml file like how SpriteFrameName works? The xml file would contain keys for each state which store the move to be played from that state.

Of course it’s possible! it may be difficult, but it is certainly possible…

Say you were doing it for noughts and crosses (tic tac toe for our American cousins).
If you enumerated each possible position and decided which of the nine squares the AI should choose, then in the XML you would have something like

<key>_________</key>
<integer>5</integer>
<key>____O____</key>
<integer>1</integer>

Where there is an underline for each square, and the AI is playing X

so you’d look up the value for the current state, and use the integer value to determine which square to play in.

Thanks for the reply. I’ve already figured out how to get the game states and the next move, and they’re both integer values so it’ll be something like this

<key>156</key>
<integer>4</integer>

What I want to know is how do I read the values in cocos2d-x. I have no experience with XML files so I need a little help.

Oh - OK.

What you need to know is that cocos2d-X uses pList files whjich are actually XML files, so uyou can read one using

auto map = FileUtils::getInstance()->getValueMapFromFile(path);

you can then look for and manipulate the valueMap (you can also use a valueVector - just depends on your exact requirements).

Hope that helps a bit.

1 Like

first of all remember include tinyxml2.h
.#include “…/external/tinyxml2/tinyxml2.h”

for example you have the file “actors_attr.xml”

<?xml version="1.0" encoding="UTF-8" ?>
<game>
    <actors>
        <actor name ="hero">
            <blood>3</blood>
            <kill>1</kill>
        </actor>

        <actor name ="enemy">
            <blood>3</blood>
            <kill>1</kill>
        </actor>

        ...

    </actors>
...
</game>

example read xml file

void GameResource::loadFileXML()
{
    tinyxml2::XMLElement* curNode = NULL;
    tinyxml2::XMLDocument* xmlDoc;
    tinyxml2::XMLElement* rootNode;
    
    xmlDoc = new tinyxml2::XMLDocument();
    
    ssize_t* nSize = new ssize_t(0);
    const char* pXmlBuffer = (const char*)FileUtils::getInstance()->getFileData("actors_attr.xml", "rb", nSize);
    if(NULL == pXmlBuffer)
    {
        //CCLOG("can not read xml file");
        return;
    }
    xmlDoc->Parse(pXmlBuffer);
    delete[] pXmlBuffer;
    // get root node
    rootNode = xmlDoc->RootElement();
    
    // find the node
    curNode = (rootNode)->FirstChildElement();
    while (NULL != curNode)
    {
        const char* nodeName = curNode->Value();
        if (strcmp(nodeName,"actors") == 0)
        {
            loadacts(curNode);
        }
        curNode = curNode->NextSiblingElement();
    }
    
}

void GameResource::loadacts(tinyxml2::XMLElement* actorsNode)
{
    tinyxml2::XMLElement* curNode;
    
    curNode = actorsNode->FirstChildElement();
    while (NULL != curNode)
    {
        const char* nodeName = curNode->Value();
        if (strcmp(nodeName, "actor") == 0)
        {
            loadact(curNode);
        }
        
        curNode = curNode->NextSiblingElement();
    }
}
void GameResource::loadact(tinyxml2::XMLElement* actorNode)
{


    //attr of one actor...
    string actorName;
    int blood;
    int kill;

    const tinyxml2::XMLAttribute* attr = actorNode->FirstAttribute();
    while (NULL != attr)
    {
        if (strcmp(attr->Name(), "name") == 0) {

            //get name
            actorName = attr->Value();
       }
     attr = attr->Next();
    }
    
    tinyxml2::XMLElement* curNode;
    curNode = actorNode->FirstChildElement();
    while (NULL != curNode)
    {
        const char* nodeName = curNode->Value();
        const char* str = curNode->GetText();

        //get attributes of actor

        if (strcmp(nodeName, "blood") == 0)
        {
            blood = atoi(str);
        }
        else if (strcmp(nodeName, "kill") == 0)
        {
           kill = atoi(str);
        }
        
        curNode = curNode->NextSiblingElement();
    }

    // save one actor with his attributes value in  array here..
    
}

1 Like

Does the file have to be a plist or can it be any xml file?

I honestly don’t know. I always use plist files because that’s what tends to be used in cocos2d-x (and on the Mac in general)

AFAIK a plist file can be an xml file - just needs the headers specified

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

then the rest of the XML obviously has to meet standards for a pList. (e.g. using particular tags that plist understands)

So it can’t be just any old XML

Alright, so I can either generate my AI into a plist file and use the in-built cocos2d-x functions to read it or I can generate a xml and use @doanhieuthien’s code to read it.

Well, these aren’t the only two options, but essentially yes.

For other options, you could put your AI into any type of file and develop (or use libraries) methods to load the information.

You could use scripting and load the script.

You could potentially load binary code and execute it!

Hi Doan,

Thanks a lot for the code it’s really helpful.

At this line I am getting a warning [quote=“doanhieuthien, post:5, topic:21913”]
const char* pXmlBuffer = (const char*)FileUtils::getInstance()-&gt;getFileData("actors_attr.xml", "rb", nSize);
[/quote]
Saying that getFileData is deprecated.

What should I use instead of it?

I’ve got the XML file (233 KB) and I used the following code -
In init function -

xmlDoc = new tinyxml2::XMLDocument();
ssize_t* nSize = new ssize_t(0);
const char* pXmlBuffer = (const char*)FileUtils::getInstance()->getFileData("AI.xml", "rb", nSize);
xmlDoc->Parse(pXmlBuffer);
delete[] pXmlBuffer;
dif = UserDefault::getInstance()->getIntegerForKey("Difficulty",4);
rootNode = xmlDoc->RootElement();

then in the playAI() method -

auto stateNode = rootNode->FirstChildElement(state); //state is a const char*
move = playAIMove(stateNode,"Moves");

And this is the playAIMove() method -

int SplitMain::playAIMove(tinyxml2::XMLElement *state, const char* nodeName)
{
    auto movesNode = state->FirstChildElement(nodeName);
    int no;
    movesNode->QueryIntAttribute("Number", &no);
    auto rdm = RandomHelper::random_int(1,no);
    auto curNode = movesNode->FirstChildElement();
    for(int c=1;c<rdm;c++)
        curNode = curNode->NextSiblingElement();
    int ret;
    curNode->QueryIntText(&ret);
    return ret;
}

After I play my first move the game takes really long to load and play its first move (about a minute) and after I play my second move it crashes.

Any idea how I can solve this?

I think you should read the file .xml and load informations to the memory only one time at begin… (load all variables into the array of class - ex:Vector< ClassOfCharacter* > )
if necessary show the “loading…” during loading.

And then just working with array in memory only, not access to file more than one time…

Thanks.
I’ll try that although I’m not sure how I’ll do it.

Turns out the delay was due to using RandomHelper::random_real().
I switched it for RandomHelper::random_int() and I was able to do the AI within the game itself without the need of loading it from a file :grin: