view mappath.c @ 945:81184d58133c aprilli2011

Sync.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 14 May 2010 11:18:20 +0000
parents 304a50a9d51f
children 667ce752bfe3
line wrap: on
line source

/*
 * Calculate "optimal" walkpath through ASCII map
 * Programmed by Matti 'ccr' Hämäläinen (Ggr Pupunen)
 * (C) Copyright 2007 Tecnic Software productions (TNSP)
 */
#include "maputils.h"
#include "th_args.h"
#include "th_string.h"
#include <string.h>

char    *srcFilename = NULL,
    *destFilename = NULL;

typedef float pathmap_t;

int    optRace = -1,
    optX0 = -1, optY0 = -1,
    optX1 = -1, optY1 = -1;
float    optTurnFactor = 5.0f;

#define MAXCOSTS    (30)
#define MAXPIECES    (256)

typedef struct {
    char *name;
    float defcost;
    char *pieces[MAXCOSTS];
    pathmap_t epcost[MAXCOSTS];
} costinfo_t;

#define T_IMP        "!^VCc?%"
#define T_ROAD        "-/|\\=+"

static const costinfo_t costTable[] = {
{ "duck", 105.0f,
    { T_IMP    , "~SrRl"    , T_ROAD, ".y"    , "fF"    , "hH"    },
    { -1    , 1.00        , 0.20    , 0.50    , 3.00    , 2.0    }},

/* Special costs for city maps */
{ "CITY", 0.1f,
    { "#~"  },
    { -1 }},

};
static const int ncostTable = (sizeof(costTable) / sizeof(costinfo_t));


/* Arguments
 */
optarg_t optList[] = {
    { 0, '?', "help",    "Show this help", OPT_NONE },
    { 1, 'v', "verbose",    "Be more verbose", OPT_NONE },
    { 2, 'q', "quiet",    "Be quiet", OPT_NONE },
    { 3, 's', "start",    "Start coords x:y", OPT_ARGREQ },
    { 4, 'e', "end",    "End coords x:y", OPT_ARGREQ },
    { 5, 'o', "output",    "Output filename", OPT_ARGREQ },
    { 6, 'r', "race",    "Race", OPT_ARGREQ },
    { 7, 'f', "factor",    "Turn factorial", OPT_ARGREQ },
};

const int optListN = (sizeof(optList) / sizeof(optarg_t));


void argShowHelp(void)
{
    int i;
    th_args_help(stderr, optList, optListN, th_prog_name,
    "[options] <input mapfile> -r <race> -s <x>:<y> -e <x>:<y>");
    
    fprintf(stderr, "Available races:\n");
    for (i = 0; i < ncostTable; i++) {
        fprintf(stderr, "%s", costTable[i].name);
        if (i != (ncostTable - 1)) fprintf(stderr, ",");
        if (i % 5 == 0)
            fprintf(stderr, "\n");
        else
            fprintf(stderr, " ");
    }
}

BOOL argParseCoord(char *arg, int *x, int *y, char *msg)
{
    BOOL res = TRUE;
    char *s = th_strdup(arg);
    char *d = strchr(s, ':');
    if (*d) {
        *d = 0; d++;
        if (th_isdigit(*s) && th_isdigit(*d)) {
            *x = atoi(s);
            *y = atoi(d);
        } else
            res = FALSE;
    } else
        res = FALSE;
    
    th_free(s);
    
    if (res) {
        THMSG(2, "Using %s coordinates %d:%d\n",
                msg, optX0, optY0);
    } else {
        THERR("Invalid %s coordinate argument '%s'!\n", msg, arg);
    }
    
    return res;
}

BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    int i;
    
    switch (optN) {
    case 0:
        argShowHelp();
        exit(0);
        break;

    case 1:
        th_verbosityLevel++;
        break;
    
    case 2:
        th_verbosityLevel = -1;
        break;
    
    case 3:
        if (!argParseCoord(optArg, &optX0, &optY0, "start"))
            return FALSE;
        break;
        
    case 4:
        if (!argParseCoord(optArg, &optX1, &optY1, "end"))
            return FALSE;
        break;
    
    case 5:
        destFilename = optArg;
        THMSG(2, "Output file set to '%s'.\n", destFilename);
        break;
    
    case 6:
        optRace = -1;
        for (i = 0; i < ncostTable && optRace < 0; i++) {
            if (strcmp(optArg, costTable[i].name) == 0)
                optRace = i;
        }
        if (optRace < 0) {
            THERR("Invalid/unsupported race name '%s'.\n", optArg);
            return FALSE;
        } else
            THMSG(2, "Race set to '%s' (%d).\n", optArg, optRace);
        break;
    
    case 7:
        optTurnFactor = atof(optArg);
        THMSG(2, "Turn factor = %1.3f.\n", optTurnFactor);
        break;
    
    default:
        THERR("Unknown option '%s'.\n", currArg);
        return FALSE;
    }
    
    return TRUE;
}


BOOL argHandleFile(char *currArg)
{
    if (!srcFilename)
        srcFilename = currArg;
    else {
        THERR("Too many input map files specified!\n");
        return FALSE;
    }

    return TRUE;
}

BOOL checkCoords(int w, int h, int x, int y)
{
    return (x >= 0 && x < w && y >= 0 && y < h);
}

BOOL checkMap(pathmap_t *pathMap, int w, int h, int x, int y, pathmap_t val)
{
    BOOL res = checkCoords(w, h, x, y);
    if (res) {
        pathmap_t tmp = pathMap[(y * w) + x];
        res = ((tmp > 0) && (tmp < val));
    }
    return res;
}

int getType(const costinfo_t *race, char c)
{
    int i;
    
    for (i = 0; i < MAXCOSTS; i++)
    if (race->pieces[i]) {
        if (strchr(race->pieces[i], c))
            return i;
    } else
        return -1;
    
    return -1;
}


/* Main program
 */
int main(int argc, char *argv[])
{
    FILE *outFile;
    mapblock_t *map, *map2;
    pathmap_t *pathMap, *d;
    const costinfo_t *race;
    pathmap_t costs[MAXPIECES];
    BOOL costSet[MAXPIECES], pathOK, found;
    int i, x, y, px, py, sx, sy, count;
    unsigned char *c;
    size_t pathSize;

#define QSET(ZX,ZY,ZV) pathMap[((ZY) * map->w) + (ZX)] = (ZV)
#define QCHK(ZX,ZY) checkMap(pathMap, map->w, map->h, (ZX), (ZY), 99999)

    /* Initialize */
    th_init("mappath", "ASCII map path generator", "0.1", NULL, NULL);
    th_verbosityLevel = 1;
    
    /* Parse arguments */
    if (!th_args_process(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, TRUE))
        exit(1);
    
    if (srcFilename == NULL) {
        THERR("No mapfile given. (try --help)\n");
        exit(0);
    }
    
    /* Check settings */
    if (optRace < 0) {
        THERR("You must set a race! (try --help for a list)\n");
        exit(3);
    }
    
    if (optX0 < 0 || optX1 < 0 || optY0 < 0 || optY1 < 0) {
        THERR("Starting and ending coordinates must be set.\n");
        exit(4);
    }
    
    /* Create cost table */
    THMSG(1, "Building pathcost table\n");
    race = &costTable[optRace];
    for (i = 0; i < MAXPIECES; i++) {
        costSet[i] = FALSE;
        costs[i] = race->defcost;
    }
    
    for (i = 0; i < MAXCOSTS; i++)
    if (race->pieces[i]) {
        c = (unsigned char *) race->pieces[i];
        while (*c) {
            if (costSet[*c]) {
                THERR("Cost for piece '%c' duplicated in #%d '%s' of race '%s'.\n",
                    (char) *c, i, race->pieces[i], race->name);
            }
            //fprintf(stderr, "'%c' -> %1.3f\n", *c, race->epcost[i]);
            costs[*c] = race->epcost[i];
            costSet[*c] = TRUE;
            c++;
        }
    }
    
    /* Read input file */
    THMSG(1, "Reading map file '%s'\n", srcFilename);
    
    if ((map = parseFile(srcFilename, FALSE)) == NULL) {
        THERR("Error reading map file '%s'!\n",
            srcFilename);
        exit(1);
    }
    
    if ((map2 = allocBlock(map->w, map->h)) == NULL) {
        THERR("Could not allocate map2!\n");
        exit(4);
    }
    
    memcpy(map2->d, map->d, map->w * map->h * sizeof(char));
    
    pathSize = (map->w) * (map->h) * sizeof(pathmap_t);
    if ((pathMap = th_malloc(pathSize)) == NULL) {
        THERR("Could not allocate pathmap!\n");
        exit(2);
    }
    
    /* Initialize and mark impassable blocks */
    memset(pathMap, 0, pathSize);
    d = pathMap;
    c = (unsigned char *) map->d;
    for (y = 0; y < map->h; y++)
    for (x = 0; x < map->w; x++) {
        if (costs[*c] < 0)
            *d = 9999999.0;
        c++; d++;
    }

    /* Create pathmap */
    THMSG(1, "Analyzing %dx%d area ...\n",
        map->w, map->h);
    
    QSET(optX0, optY0, 1);
    QSET(optX1, optY1, 0);
    pathOK = TRUE;
    while (pathOK) {
        pathOK = FALSE;
        c = (unsigned char *) map->d;
        d = pathMap;
        
        for (y = 0; y < map->h; y++)
        for (x = 0; x < map->w; x++) {
            if (*d == 0.0f) {
                int dx, dy, nx, ny, mx, my;
                pathmap_t mval = 9999999.0f, nval;
                
                found = FALSE;
                for (dy = -1; dy <= 1; dy++)
                for (dx = -1; dx <= 1; dx++) {
                    nx = x + dx;
                    ny = y + dy;
                    
                    if (!(dx == 0 && dy == 0) &&
                       checkMap(pathMap, map->w, map->h, nx, ny, mval)) {
                        mval = nval;
                        mx = nx;
                        my = ny;
                        found = TRUE;
                    }
                }
                
                if (found) {
                    pathmap_t v;
                    
                    if (x == optX1 && y == optY1)
                        goto pathResolved;
                    
                    v = mval + costs[*c];
                    
                    if (getType(race, *c) != getType(race, map->d[(my * map->w) + mx]))
                        v += 15.0f;
                    
                    QSET(x, y, v);
                    pathOK = TRUE;
                }
            }
            c++; d++;
        }
    }
    
    THMSG(1, "Bailout!\n");

pathResolved:
    QSET(optX1, optY1, 989898.0f);
#if 0
    d = pathMap;
    c = (unsigned char *) map->d;
    for (y = 0; y < map->h; y++) {
        for (x = 0; x < map->w; x++) {
            printf("%10.2f[%c] ", *d, *c);
            c++; d++;
        }
        printf("\n");
    }
#endif

    THMSG(1, "Resolving %d:%d -> %d:%d ...\n", optX0, optY0, optX1, optY1);
    sx = x = optX1; sy = y = optY1;
    px = py = 0;
    count = 0;
    do {
        int dx, dy, nx = 0, ny = 0;
        pathmap_t val, oval = pathMap[(y * map->w) + x];
        val = 9999999.0f;
        
        found = FALSE;
        for (dy = -1; dy <= 1; dy++)
        for (dx = -1; dx <= 1; dx++)
        if (!(dx == 0 && dy == 0)) {
            int zx = x+dx, zy = y+dy;
            if (checkCoords(map->w, map->h, zx, zy)) {
                pathmap_t nval = pathMap[(zy * map->w) + zx];
                if (nval <= val && nval > 0) {
                    val = nval;
                    nx = dx;
                    ny = dy;
                    found = TRUE;
                }
            }
        }
        
        if (found) {
            //printf("%d,%d -> %d,%d [%1.3f -> %1.3f]\n", x, y, nx, ny, oval, val);
            if (px != nx || py != ny) {
                px = nx;
                py = ny;
                sx = x;
                sy = y;
            }
            x += nx;
            y += ny;
            map2->d[(y * map->w) + x] = '&';
        } else {
            printf("return: %d,%d -> %d,%d\n", x, y, sx, sy);
            x = sx;
            y = sy;
        }
        
        count++;
        
    } while ((x != optX0 || y != optY0) && count < 5000);
    
    /* Open output file */
    if (destFilename == NULL)
        outFile = stdout;
    else if ((outFile = fopen(destFilename, "wb")) == NULL) {
        THERR("Error opening output file '%s'!\n",
            destFilename);
        exit(1);
    }
    
    printBlock(outFile, map2);
    fclose(outFile);
    
    THMSG(1, "Done.\n");
    
    exit(0);
    return 0;
}