Mercurial > hg > batmud > maputils
view mkmap.c @ 30:2eceda1c86ab
Fixes
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 10 Dec 2006 02:38:23 +0000 |
parents | a49a85e4a043 |
children | 25e473a82ce3 |
line wrap: on
line source
/* * mkmap - Compute complete ASCII map by stitching pieces * together with a semi-heuristic+fallback bruteforce algorithm * * Programmed by Matti 'ccr' Hämäläinen (Ggr Pupunen) * (C) Copyright 2006 Tecnic Software productions (TNSP) */ #include "maputils.h" #include <string.h> #include <strings.h> #include "th_args.h" #include "th_string.h" #define MAX_FILES (256) /* Variables */ DCHAR *progName = NULL; DINT nsrcFiles = 0; DCHAR *srcFiles[MAX_FILES], *destFile = NULL; DINT optBlockW = 21, optBlockH = 21, optMapFactor = 2, optRounds = 100; DBOOL optInverse = FALSE, optHardDrop = FALSE; DFLOAT optMatch = 90.0; DCHAR *optPattern = "You quack 'pfs.'"; DCHAR *optInitialMap = NULL; /* Arguments */ t_opt optList[] = { { 0, '?', "help", "Show this help", 0 }, { 1, 'o', "output", "Specify output file", 1 }, { 2, 'v', "verbose", "Be more verbose", 0 }, { 3, 'q', "quiet", "Be quiet", 0 }, { 4, 'm', "match", "Match percentage", 1 }, { 5, 'r', "rounds", "Processing timeout # rounds", 1 }, { 6, 's', "start", "Map block start marker string", 1 }, { 7, 'i', "inverse", "Use inverse search of blocks", 0 }, { 11,'d', "drop", "Use hard dropping (bailout on first mismatch)", 0 }, { 8, 'w', "width", "Block width", 1 }, { 9, 'h', "height", "Block height", 1 }, { 10,'I', "initial", "Initial map file", 1 }, }; const DINT optListN = (sizeof(optList) / sizeof(t_opt)); void argShowHelp() { th_args_help(stdout, optList, optListN, progName, "[options] <inputfile> [inputfile#2..]"); } void argHandleOpt(const DINT optN, DCHAR *optArg, DCHAR *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 1: if (optArg) { destFile = optArg; THMSG(1, "Output file '%s'\n", destFile); } else { THERR("No output filename argument provided.\n"); exit(1); } break; case 2: th_verbosityLevel++; break; case 3: th_verbosityLevel = -1; break; case 4: if (optArg) { optMatch = atof(optArg); THMSG(1, "Match at %1.4f%%\n", optMatch); } break; case 5: if (optArg) { optRounds = atoi(optArg); THMSG(1, "Processing rounds timeout %d\n", optRounds); } break; case 6: if (optArg) { optPattern = optArg; THMSG(1, "Map block start marker '%s'\n", optPattern); } break; case 7: optInverse = TRUE; THMSG(1, "Using inverse processing of blocks.\n"); break; case 8: if (optArg) { optBlockW = atoi(optArg); THMSG(1, "Map block width = %d\n", optBlockW); } break; case 9: if (optArg) { optBlockH = atoi(optArg); THMSG(1, "Map block height = %d\n", optBlockH); } break; case 10: if (optArg) { optInitialMap = optArg; THMSG(1, "Using initial map file = '%s'\n", optArg); } break; case 11: optHardDrop = TRUE; THMSG(1, "Using hard dropping (instant bailout on mismatch).\n"); break; default: THERR("Unknown argument '%s'.\n", currArg); exit(3); break; } } void argHandleFile(DCHAR *currArg) { if (nsrcFiles < MAX_FILES) { srcFiles[nsrcFiles] = currArg; nsrcFiles++; } else { THERR("Too many input files specified (%d max)!\n", MAX_FILES); exit(2); } } /* Calculate matching percentage of given block against a map, * with using specified x/y offsets. */ DFLOAT matchBlock(mapblock_t *map, mapblock_t *match, DINT ox, DINT oy) { DINT x, y, c1, c2, dy, dx, n, k; n = k = 0; for (y = 0; y < match->h; y++) for (x = 0; x < match->w; x++) { dx = (ox + x); dy = (oy + y); k++; if (dx >= 0 && dx < map->w && dy >= 0 && dy < map->h) { c1 = match->d[(y * match->w) + x]; c2 = map->d[(dy * map->w) + dx]; if (c1 == 0 && c2 == 0) { /* Both are zero (nothing) */ } else if (c1 == c2) { /* Exact match, increase % */ n++; } else if (c1 == 0 || c2 == 0) { /* Either is zero */ } else if (optHardDrop) { /* Mismatch, return failure */ return -1; } } } if (k > 0) return ((DFLOAT) n * 100.0f) / (DFLOAT) k; else return 0.0f; } /* Clean given block from position markers and whitespaces * TODO: specify position marker(s) somehow instead of hardcoding */ void cleanBlock(mapblock_t *map) { DINT x, y, o; o = 0; for (y = 0; y < map->h; y++) for (x = 0; x < map->w; x++) { switch (map->d[o]) { case ' ': case '*': // case '@': map->d[o] = 0; break; case 'X': map->d[o] = '.'; break; } o++; } } /* Parse next block (marked by string optPattern) from * input stream into a mapblock, return NULL if not found or error. */ mapblock_t * parseBlock(FILE *inFile, DINT inW, DINT inH, DCHAR *inPattern) { mapblock_t *tmp; DINT x, y, o; DBOOL isFound; DCHAR s[4096]; isFound = FALSE; while (!feof(inFile) && !isFound) { fgets(s, sizeof(s), inFile); if (!strncmp(s, inPattern, strlen(inPattern))) isFound = TRUE; } if (!isFound) { THERR("No block start marker found.\n"); return NULL; } if ((tmp = allocBlock(inW, inH)) == NULL) { THERR("Could not allocate mapblock (%d, %d)\n", inW, inH); return NULL; } o = y = x = 0; while (!feof(inFile) && (y < tmp->h)) { DUINT i; fgets(s, sizeof(s), inFile); for (i = 0; i < sizeof(s) && s[i] && s[i] != '\r' && s[i] != '\n'; i++); s[i] = 0; if (strlen(s) != (DUINT) tmp->w) { THERR("Broken block, line width %d < %d!\n", strlen(s), tmp->w, s); freeBlock(tmp); return NULL; } for (x = 0; x < tmp->w; x++) { tmp->d[o++] = s[x]; } y++; } if (y < tmp->h) { THERR("Broken block, height %d < %d\n", y, tmp->h); freeBlock(tmp); return NULL; } return tmp; } /* Bruteforce search / block matching .. */ DBOOL blockSearchBrute(mapblock_t *map, mapblock_t *b, DINT *x, DINT *y, DFLOAT m) { DINT ox, oy; DFLOAT v; for (oy = -(b->h); oy < (map->h + b->h); oy++) for (ox = -(b->w); ox < (map->w + b->w); ox++) { v = matchBlock(map, b, ox, oy); if (v >= m) { *x = ox; *y = oy; return TRUE; } } return FALSE; } DBOOL blockSearch(mapblock_t *map, mapblock_t *b, DINT *x, DINT *y, DFLOAT m) { DINT ox, oy; DFLOAT v; for (oy = (*y - b->h); oy < (*y + b->h); oy++) for (ox = (*x - b->w); ox < (*x + b->w); ox++) { v = matchBlock(map, b, ox, oy); if (v >= m) { *x = ox; *y = oy; return TRUE; } } return blockSearchBrute(map, b, x, y, m); } int main(int argc, char *argv[]) { FILE *tmpFile; DBOOL isOK; DINT i, n, ox, oy, nmapBlocks = 0; mapblock_t *map = NULL, *tmp = NULL, *initial = NULL; mapblock_t **mapBlocks = NULL; progName = argv[0]; th_init("mkmap", "ASCII Map Auto-Stitcher", "0.3", NULL, NULL); th_verbosityLevel = 1; /* Parse arguments */ th_args_process(argc, argv, optList, optListN, argHandleOpt, argHandleFile); if (nsrcFiles < 1) { THERR("Nothing to do. (try --help)\n"); exit(0); } /* Read initial map */ if (optInitialMap) { THMSG(1, "Reading initial map '%s'\n", optInitialMap); initial = parseFile(optInitialMap); if (initial) { THMSG(2, "Initial dimensions %d x %d\n", initial->w, initial->h); } else { THERR("Initial map could not be loaded!\n"); exit(1); } } /* Read in continuous mapdata and parse it into map blocks */ for (i = 0; i < nsrcFiles; i++) { if ((tmpFile = fopen(srcFiles[i], "rb")) == NULL) { THERR("Error opening input file '%s'!\n", srcFiles[i]); exit(1); } while (!feof(tmpFile)) { if ((tmp = parseBlock(tmpFile, optBlockW, optBlockH, optPattern)) != NULL) { nmapBlocks++; mapBlocks = (mapblock_t **) th_realloc(mapBlocks, sizeof(mapblock_t *) * nmapBlocks); if (!mapBlocks) { THERR("Could not allocate/extend mapblock pointer structure (#%d)\n", nmapBlocks); exit(3); } cleanBlock(tmp); mapBlocks[nmapBlocks - 1] = tmp; } } fclose(tmpFile); } THMSG(1, "Total of %d mapblocks read.\n", nmapBlocks); /* Start matching the blocks with bruteforce */ if (initial) { map = initial; } else if (nmapBlocks > 0) { if (optInverse) tmp = mapBlocks[nmapBlocks - 1]; else tmp = mapBlocks[0]; if ((map = allocBlock(tmp->w * optMapFactor, tmp->h * optMapFactor)) == NULL) { THERR("Could not allocate initial map.\n"); return 3; } putBlockDo(map, tmp, (map->w / 2) - (tmp->w / 2), (map->h / 2) - (tmp->h / 2)); } THMSG(1, "Initialized initial map of (%d x %d)\n", map->w, map->h); n = 0; isOK = FALSE; while ((n < optRounds) && !isOK) { DFLOAT mv = optMatch; n++; THPRINT(2, "Round #%d [%1.2f%%]: ", n, mv); isOK = TRUE; for (i = 0; i < nmapBlocks; i++) if (!mapBlocks[i]->mark) { if (blockSearch(map, mapBlocks[i], &ox, &oy, mv)) { THPRINT(2, "X"); if (putBlock(&map, mapBlocks[i], ox, oy) < 0) { THERR("putBlock(%d, %d, %d) failed!\n", ox, oy, i); } isOK = FALSE; } else { THPRINT(2, "."); } } THPRINT(2, "\n"); } /* Output generated map */ if (map) { THMSG(1, "Outputting generated map ...\n"); if (destFile == NULL) tmpFile = stdout; else if ((tmpFile = fopen(destFile, "wb")) == NULL) { THERR("Error opening output file '%s'!\n", destFile); exit(1); } printBlock(tmpFile, map); fclose(tmpFile); for (n = i = 0; i < nmapBlocks; i++) if (!mapBlocks[i]->mark) n++; THMSG(1, "%d mapblocks unused/discarded\n", n); } else { THERR("No map generated?\n"); } return 0; }