DerekSchaefer.NET I do stuff, you read about it!

30Jul/102

Web Development Using Django, a Python-Based Framework

Recently, I've been using Django at work to develop a corporate web application. This is first time that I have used Django for anything this extensive, and it has been a positively pleasant and, what is probably more important, incredibly productive experience.

Some descriptive excerpts from Wikipedia:

Django is an open source web application framework, written in Python, which follows the model-view-controller (MVC) architectural pattern.

Django's primary goal is to ease the creation of complex, database-driven websites. Django emphasizes reusability and "pluggability" of components, rapid development, and the principle of DRY (Don't Repeat Yourself). Python is used throughout, even for settings, files, and data models.

...and Django is quite good at those things. And of course, being written in Python (and using the mod_wsgi Apache module), it is also very fast and efficient.

Additionally, if you are looking for a good CMS for Django, you will likely find, unsurprisingly, Django CMS. It is a very elegant solution that is very extensible and easy to setup. I highly recommend it.

25Apr/101

Starcraft AI: Misbehaving Workers

Since we changed the official tournament map to The Fastest Possible Map V.2, the built in Worker Manager has not been gathering resources correctly (or at all, really). The problem arises from the map breaking Starcraft layout and positioning rules in order to be as fast as possible; minerals and geysers overlap, bases are placed impossibly close to resources, etc. I have created a fix that actually ends up making the worker AI more efficient, and gives you more control over how your workers behave.

There are only a few steps. First, copy this code:

// When an assimilator is constructed, assign workers to it
for (std::set<std::pair<Unit*, int> >::iterator i = workersPerAssimilator.begin(); i != workersPerAssimilator.end(); i++)
{
    // If the assimilator is still being constructed, or WORKERS_PER_ASSIMILATOR workers are
    // already asigned to it, ignore it for now
    if (!((*i).first->isCompleted()) || (*i).second >= WORKERS_PER_ASSIMILATOR) continue;

    // Assign a maximum of WORKERS_PER_ASSIMILATOR workers to each available assimilator
    int probeCount = 0;
    UnitGroup tempGasWorkers = SelectAll(Broodwar->self(), UnitTypes::Protoss_Probe)(FilterFlag::isIdle) + SelectAll(Broodwar->self(), UnitTypes::Protoss_Probe)(FilterFlag::isGatheringMinerals);
    tempGasWorkers = tempGasWorkers - gasWorkers;
    for (UnitGroup::iterator w = tempGasWorkers.begin(); w != tempGasWorkers.end() && probeCount < WORKERS_PER_ASSIMILATOR; w++, probeCount++)
    {
        (*w)->stop();
        (*w)->rightClick((*i).first);
        gasWorkers.insert((*w));
        (*i).second = (*i).second + 1;
    }
}

// Make sure all idle workers are always gathering minerals
UnitGroup workers = SelectAll(Broodwar->self(), UnitTypes::Protoss_Probe)(FilterFlag::isIdle);
workers = workers - gasWorkers;
for each (Unit* w in workers)
{
    w->rightClick(closestMinerals);
}

...and then place it in the onFrame event handler immediately following this chunk of code:

if (Broodwar->isReplay()) return;
if (!this->analyzed) return;
this->buildManager->update();
this->buildOrderManager->update();
this->baseManager->update();
this->workerManager->update();
this->techManager->update();
this->upgradeManager->update();
this->supplyManager->update();
this->scoutManager->update();
this->defenseManager->update();
this->arbitrator.update();

this->enhancedUI->update();

If you will not be playing as Protoss, be sure to replace all occurrences of UnitTypes::Protoss_Probe with your race's worker type.

I also suggest that you only have this code execute every second or half a second, for performance reasons. It really does not need to be executed every frame. You can do this by placing the above code that you just copied within the following condition:

// Only do these every second, for performance reasons
if (Broodwar->getFrameCount() % 24 == 0)
{
    // Put all that code here
}

Next, place the following global variables at the very top of your BasicAIModule.cpp file:

// Starting base location
BWTA::BaseLocation* home;
// All workers assigned to gathering gas
UnitGroup gasWorkers;
// Set of all assimilators and the number workers assigned to them
std::set<std::pair<Unit*, int> > workersPerAssimilator;
// Closest minerals to start location
Unit* closestMinerals;

int WORKERS_PER_ASSIMILATOR = 3;

Set this to whatever you feel is appropriate.

Finally, place this code in your onUnitMorph:

// Once an assimilator has been constructed, add it to the set of assimilators
if (unit->getPlayer() == Broodwar->self() && unit->getType() == UnitTypes::Protoss_Assimilator)
{
    workersPerAssimilator.insert(std::pair<Unit*, int>(unit, 0));
}

Your workers will now behave efficiently and as expected!

Note: If you're still having problems, please see the full template here.

14Apr/100

All2SQL++

Progress is being made on my C++ port of All2SQL. The algorithm itself is now fully implemented in C++, with the addition of several improvements, both for efficiency and features.

I am now working on a way to make the type-definition system more modular and extensible. The type-definition system is what allows the algorithm to determine what type a given cell is (obviously, very important). On one hand, I will eventually support all MySQL data types, for performance and convenience. But if users define their own data types, it would of course be useful to quickly create a definition that All2SQL could make use of. That's why, so far, I believe I will implement a rudimentary plug-in system for creating custom definitions.

Here you can see All2SQL being run on the command line, and displaying a list of its parameters:

C:\All2SQL.exe --help

USAGE:

   All2SQL.exe  -f  [-d ] [-e ] [-c ] [-o ] [-m] [--] [--version] [-h]

Where:

   -f ,  --filename
     (required)  File name

   -d ,  --delimiter
     Cell delimiting character

   -e ,  --encloser
     Cell enclosing character

   -c ,  --escape
     Cell data escape character

   -o ,  --output
     Output file name

   -m,  --mysql
     Output MySQL table definition

   --,  --ignore_rest
     Ignores the rest of the labeled arguments following this flag.

   --version
     Displays version information and exits.

   -h,  --help
     Displays usage information and exits.

   All2SQL analyzes CSV files to find the optimal MySQL data type
   representation.
6Mar/100

Resuming work on All2SQL

After an unfortunately long hiatus, I will soon finally have time to continue working on All2SQL.

All2SQL is a PHP library that converts any parsable format to valid SQL insertion code, allowing you to import any data source into your database. All you need is a parser! The MySQL platform is supported, with more to be added.

The algorithm and design are derived from my work on phpMyAdmin during my participation in Google's Summer of Code.

My original plan for All2SQL was to implement it solely as a stand-alone PHP library, but after some more research and consideration, I believe that an unmanaged, platform-independent C++ library would actually be of more value. So this is what I will be focusing on for the time being. Although, I may decide to port it to PHP in the future.

Obviously, writing the library in C++ would be significantly more efficient than in PHP, which is especially important when working with large data-sets. This would also allow me to create a stand-alone command-line executable, which would be easily scriptable by end-users.

The project will be available as a command-line utility, as well as both static (.a on Linux, .lib on Windows) and dynamic (.so on Linux, .dll on Windows) libraries.

I will post updates regarding my progress here.

Stay tuned!

10Feb/101

BOOST_FOREACH

In our efforts to make our StarCraft AI tournament more accessible to the masses (specifically those that are not confident in their C++ abilities), we turned to Boost.

If you haven't heard of Boost, and/or don't know what it is, read this blurb taken from the Boost website:

Boost provides free peer-reviewed portable C++ source libraries.

We emphasize libraries that work well with the C++ Standard Library. Boost libraries are intended to be widely useful, and usable across a broad spectrum of applications. The Boost license encourages both commercial and non-commercial use.

We aim to establish "existing practice" and provide reference implementations so that Boost libraries are suitable for eventual standardization. Ten Boost libraries are already included in the C++ Standards Committee's Library Technical Report (TR1) and will be in the new C++0x Standard now being finalized. C++0x will also include several more Boost libraries in addition to those from TR1. More Boost libraries are proposed for TR2.

Essentially, Boost is all about code reuse, increased productivity, and efficiency.

One of the many incredible Boost libraries that we have made extensive use of is BOOST_FOREACH, which allows you to turn unsightly iterator-based loops such as:

... into this:

Nice, right?

I also defined "foreach" to be "BOOST_FOREACH" to make my code even more concise.

You can find the BOOST_FOREACH documentation here.