From bf396f111317663bba3950e57968af19f2f56a44 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Fri, 30 Jan 2015 06:58:04 +0100 Subject: [PATCH] Server/AI: Explore map for (long-time) unexplored cells. --- SERVER_COMMANDS | 4 ++ roguelike-server.do | 1 + src/server/ai.c | 54 +++++++++++++++---------- src/server/ai.h | 4 +- src/server/field_of_view.c | 54 +++++++++++++++++++------ src/server/field_of_view.h | 4 +- src/server/god_commands.c | 22 ++++++++--- src/server/hardcoded_strings.c | 3 +- src/server/hardcoded_strings.h | 3 +- src/server/io.c | 72 +++++++++++++++++++--------------- src/server/thing_actions.c | 2 + src/server/things.c | 1 + src/server/things.h | 1 + 13 files changed, 151 insertions(+), 74 deletions(-) diff --git a/SERVER_COMMANDS b/SERVER_COMMANDS index cca0671..754538e 100644 --- a/SERVER_COMMANDS +++ b/SERVER_COMMANDS @@ -173,6 +173,10 @@ T_MEMMAP [0 to 255] [string] Set part of selected thing's memory of the game map to string argument: the line of the argument's number. +T_MEMDEPTHMAP [0 to 255] [string] +Set part of selected thing's game map memory depth map to string argument: the +line of the argument's number. + T_MEMTHING [0 to 255] [0 to 255] [0 to 255] Add to selected thing's memory of things on map thing of ID of first argument, y position of second argument and x position of third argument. diff --git a/roguelike-server.do b/roguelike-server.do index 7a6a3bc..ebe418d 100644 --- a/roguelike-server.do +++ b/roguelike-server.do @@ -6,4 +6,5 @@ redo-ifchange build/build_template TARGET=server +LIBRARY_LINKS=-lm . ./build/build_template diff --git a/src/server/ai.c b/src/server/ai.c index dd11f38..ae56cdb 100644 --- a/src/server/ai.c +++ b/src/server/ai.c @@ -38,18 +38,19 @@ static void get_neighbor_scores(uint16_t * score_map, uint16_t pos_i, */ static void dijkstra_map(uint16_t * score_map, uint16_t max_score); -/* get_dir_to_nearest_thing() helper: Prepare "score_map" for dijkstra_map(). */ +/* get_dir_to_nearest_target() helper: Prepare "score_map" for dijkstra_map(). */ static void init_score_map(char filter, uint16_t * score_map, uint32_t map_size, struct Thing * t_eye); /* Set (if possible) as "t_eye"'s command a move to the path to the path-wise - * nearest thing that is not "t_eye" and fits criteria set by "filter". On - * success, return 1, else 0. Values for "filter": + * nearest target that is not "t_eye" and fits criteria set by "filter". On + * success, return !0, else 0. Values for "filter": * "e": thing in FOV is animate, but not of "t_eye"'s thing type; build path as * avoiding things of "t_eye"'s type - * "c": thing in memorized map is consumable. + * "c": thing in memorized map is consumable + * "s": memory map cell with greatest-reachable degree of unexploredness */ -static uint8_t get_dir_to_nearest_thing(struct Thing * t_eye, char filter); +static uint8_t get_dir_to_nearest_target(struct Thing * t_eye, char filter); /* Return 1 if any thing not "t_eye" is known and fulfills some criteria defined * by "filter", else 0. Values for "filter": @@ -189,18 +190,31 @@ static void init_score_map(char filter, uint16_t * score_map, uint32_t map_size, score_map[tm->pos.y * world.map.length + tm->pos.x] = 0; } } + else if (('0' < filter && '9' >= filter) || ' ' == filter) + { + uint32_t i; + for (i = 0; i < (uint32_t) (world.map.length * world.map.length); i++) + { + score_map[i] = filter == t_eye->mem_depth_map[i] ? 0 : score_map[i]; + } + } } -static uint8_t get_dir_to_nearest_thing(struct Thing * t_eye, char filter) +static uint8_t get_dir_to_nearest_target(struct Thing * t_eye, char filter) { - char dir_to_nearest_thing = 0; - if (seeing_thing(t_eye, filter)) + char dir_to_nearest_target = 0; + uint8_t run_i = 9 /* maximum mem depth age below never-explored */ + 1; + uint8_t mem_depth_char = ' '; + while (run_i && ('s' == filter || seeing_thing(t_eye, filter))) { + run_i = 's' != filter ? 0 : run_i - 1; uint32_t map_size = world.map.length * world.map.length; uint16_t * score_map = try_malloc(map_size * sizeof(uint16_t),__func__); - init_score_map(filter, score_map, map_size, t_eye); + init_score_map('s' == filter ? mem_depth_char : filter, + score_map, map_size, t_eye); + mem_depth_char = ' ' == mem_depth_char ? '9' : mem_depth_char - 1; dijkstra_map(score_map, UINT16_MAX-1); uint16_t neighbors[N_DIRS]; uint16_t pos_i = (t_eye->pos.y * world.map.length) + t_eye->pos.x; @@ -214,17 +228,17 @@ static uint8_t get_dir_to_nearest_thing(struct Thing * t_eye, char filter) if (min_neighbor > neighbors[i]) { min_neighbor = neighbors[i]; - dir_to_nearest_thing = dirs[i]; + dir_to_nearest_target = dirs[i]; } } + if (dir_to_nearest_target) + { + t_eye->command = get_thing_action_id_by_name(s[S_CMD_MOVE]); + t_eye->arg = dir_to_nearest_target; + run_i = 0; + } } - if (dir_to_nearest_thing) - { - t_eye->command = get_thing_action_id_by_name(s[S_CMD_MOVE]); - t_eye->arg = dir_to_nearest_thing; - return 1; - } - return 0; + return dir_to_nearest_target; } @@ -309,7 +323,7 @@ static uint8_t standing_on_consumable(struct Thing * t_standing) extern void ai(struct Thing * t) { t->command = get_thing_action_id_by_name(s[S_CMD_WAIT]); - if (!get_dir_to_nearest_thing(t, 'e')) + if (!get_dir_to_nearest_target(t, 'e')) { int16_t sel = get_inventory_slot_to_consume(t); if (-1 != sel) @@ -321,9 +335,9 @@ extern void ai(struct Thing * t) { t->command = get_thing_action_id_by_name(s[S_CMD_PICKUP]); } - else + else if (!get_dir_to_nearest_target(t, 'c')) { - get_dir_to_nearest_thing(t, 'c'); + get_dir_to_nearest_target(t, 's'); } } } diff --git a/src/server/ai.h b/src/server/ai.h index 27c1054..966edd5 100644 --- a/src/server/ai.h +++ b/src/server/ai.h @@ -19,7 +19,9 @@ struct Thing; * type); if they see none, they will consume consumables in their inventory; if * there are none, they will pick up any consumables they stand on; if they * stand on none, they will move towards the next consumable they see or - * remember on the map; if they see or remember none, they'll simply wait. + * remember on the map; if they see or remember none, they'll explore parts of + * the map unseen since ever or for at least one turn; if there is nothing to + * explore, they will simply wait. */ extern void ai(struct Thing * t); diff --git a/src/server/field_of_view.c b/src/server/field_of_view.c index d6b3a8d..8a162d8 100644 --- a/src/server/field_of_view.c +++ b/src/server/field_of_view.c @@ -6,6 +6,7 @@ */ #include "field_of_view.h" +#include /* pow() */ #include /* NULL */ #include /* uint8_t, uint16_t, uint32_t, int32_t, UINT8_MAX */ #include /* free() */ @@ -14,6 +15,7 @@ #include "../common/try_malloc.h" /* try_malloc() */ #include "../common/yx_uint8.h" /* yx_uint8 */ #include "map.h" /* mv_yx_in_dir_legal(), init_empty_map() */ +#include "rrand.h" /* rrand() */ #include "things.h" /* Thing, ThingInMemory, add_thing_to_memory_map() */ #include "world.h" /* world */ @@ -75,6 +77,11 @@ static void eval_position(uint16_t dist, uint16_t hex_i, char * fov_map, struct yx_uint8 * test_pos, struct shadow_angle ** shadows); +/* Update "t_eye"'s things-on-map memory by removing from its .t_mem all + * memorized thing in FOV, and adding inanimate things in FOV to it. + */ +static void add_things_to_map_memory(struct Thing * t_eye); + static uint32_t correct_angle(int32_t angle) @@ -262,20 +269,8 @@ static void eval_position(uint16_t dist, uint16_t hex_i, char * fov_map, -extern void update_map_memory(struct Thing * t_eye) +static void add_things_to_map_memory(struct Thing * t_eye) { - if (!t_eye->mem_map) - { - init_empty_map(&(t_eye->mem_map)); - } - uint32_t i; - for (i = 0; i < (uint32_t) (world.map.length * world.map.length); i++) - { - if (' ' == t_eye->mem_map[i] && t_eye->fov_map[i] == 'v') - { - t_eye->mem_map[i] = world.map.cells[i]; - } - } struct ThingInMemory * tm = t_eye->t_mem; struct ThingInMemory * tm_prev = NULL; struct ThingInMemory * tm_next = NULL; @@ -310,6 +305,39 @@ extern void update_map_memory(struct Thing * t_eye) +extern void update_map_memory(struct Thing * t_eye) +{ + if (!t_eye->mem_map) + { + init_empty_map(&(t_eye->mem_map)); + } + if (!t_eye->mem_depth_map) + { + init_empty_map(&(t_eye->mem_depth_map)); + } + uint32_t i; + for (i = 0; i < (uint32_t) (world.map.length * world.map.length); i++) + { + if ('v' == t_eye->fov_map[i]) + { + t_eye->mem_depth_map[i] = '0'; + if (' ' == t_eye->mem_map[i]) + { + t_eye->mem_map[i] = world.map.cells[i]; + } + continue; + } + if ( '0' <= t_eye->mem_depth_map[i] && '9' > t_eye->mem_depth_map[i] + && !(rrand() % (uint16_t) pow(2, t_eye->mem_depth_map[i] - 48))) + { + t_eye->mem_depth_map[i]++; + } + } + add_things_to_map_memory(t_eye); +} + + + extern void build_fov_map(struct Thing * t) { uint32_t map_size = world.map.length * world.map.length; diff --git a/src/server/field_of_view.h b/src/server/field_of_view.h index 41fde34..54ffe28 100644 --- a/src/server/field_of_view.h +++ b/src/server/field_of_view.h @@ -15,8 +15,8 @@ struct Thing; -/* Update "t"'s .mem_map memory with what's in its current FOV, remove from its - * .t_mem all memorized things in FOV and add inanimate things in FOV to it. +/* Update "t_eye"'s .mem_map memory with what's in its current FOV, and update + * and age the .mem_depth_map. */ extern void update_map_memory(struct Thing * t_eye); diff --git a/src/server/god_commands.c b/src/server/god_commands.c index 8756404..a76f058 100644 --- a/src/server/god_commands.c +++ b/src/server/god_commands.c @@ -449,11 +449,12 @@ extern uint8_t parse_god_command_1arg(char * tok0, char * tok1) extern uint8_t parse_god_command_2arg(char * tok0, char * tok1, char * tok2) { - if (!t && !strcmp(tok0, s[S_CMD_T_MEMMAP])) + if (!t && ( !strcmp(tok0, s[S_CMD_T_MEMMAP]) + || !strcmp(tok0, s[S_CMD_T_MEMDEPTHMAP]))) { return err_line(1, "No thing defined to manipulate yet."); } - if (!strcmp(tok0, s[S_CMD_T_MEMMAP])) + if (!strcmp(tok0,s[S_CMD_T_MEMMAP]) || !strcmp(tok0,s[S_CMD_T_MEMDEPTHMAP])) { uint8_t y = atoi(tok1); if (parsetest_int(tok1, '8') || y >= world.map.length) @@ -464,11 +465,22 @@ extern uint8_t parse_god_command_2arg(char * tok0, char * tok1, char * tok2) { return err_line(1, "Map line length is unequal map width."); } - if (!t->mem_map) + if (!strcmp(tok0,s[S_CMD_T_MEMMAP])) { - init_empty_map(&(t->mem_map)); + if (!t->mem_map) + { + init_empty_map(&(t->mem_map)); + } + memcpy(t->mem_map + y * world.map.length, tok2, world.map.length); + } + else + { + if (!t->mem_depth_map) + { + init_empty_map(&(t->mem_depth_map)); + } + memcpy(t->mem_depth_map+y*world.map.length, tok2, world.map.length); } - memcpy(t->mem_map + y * world.map.length, tok2, world.map.length); } else { diff --git a/src/server/hardcoded_strings.c b/src/server/hardcoded_strings.c index a0a2173..00b9e02 100644 --- a/src/server/hardcoded_strings.c +++ b/src/server/hardcoded_strings.c @@ -9,7 +9,7 @@ -char * s[42]; +char * s[43]; @@ -43,6 +43,7 @@ extern void init_strings() s[S_CMD_T_HP] = "T_LIFEPOINTS"; s[S_CMD_T_CARRIES] = "T_CARRIES"; s[S_CMD_T_MEMMAP] = "T_MEMMAP"; + s[S_CMD_T_MEMDEPTHMAP] = "T_MEMDEPTHMAP"; s[S_CMD_T_MEMTHING] = "T_MEMTHING"; s[S_CMD_AI] = "ai"; s[S_CMD_WAIT] = "wait"; diff --git a/src/server/hardcoded_strings.h b/src/server/hardcoded_strings.h index d110827..15d79bd 100644 --- a/src/server/hardcoded_strings.h +++ b/src/server/hardcoded_strings.h @@ -42,6 +42,7 @@ enum string_num S_CMD_T_HP, S_CMD_T_CARRIES, S_CMD_T_MEMMAP, + S_CMD_T_MEMDEPTHMAP, S_CMD_T_MEMTHING, S_CMD_AI, S_CMD_WAIT, @@ -60,7 +61,7 @@ enum string_num extern void init_strings(); -extern char * s[42]; +extern char * s[43]; diff --git a/src/server/io.c b/src/server/io.c index d28eeb6..3c75bd8 100644 --- a/src/server/io.c +++ b/src/server/io.c @@ -40,6 +40,9 @@ static void write_string(FILE * file, char * string); static void write_key_space_value(FILE * file, char * key, uint32_t value); static void write_key_space_string(FILE * file, char * key, char * string); +/* Write to "file" game-map-sized "map" in "command"-prefixed numbered lines. */ +static void write_mem_map(FILE * file, char * map, char * command); + /* Write to "file" \n-delimited line of "key" + space + "value" as string. */ static void write_thing(FILE * file, struct Thing * t); @@ -126,6 +129,33 @@ static void write_key_space_string(FILE * file, char * key, char * string) +static void write_mem_map(FILE * file, char * map, char * command) +{ + if (map) + { + uint32_t map_size = world.map.length * world.map.length;/* snprintf() */ + char * map_copy = try_malloc(map_size + 1, __func__); /* reads one */ + memcpy(map_copy, map, map_size); /* byte beyond map_size */ + map_copy[map_size] = '\0'; /* if string is not \0-terminated. */ + uint16_t y; + char string[UINT8_MAX + 1 + 1]; + for (y = 0; y < world.map.length; y++) + { + int test = snprintf(string, world.map.length + 1, "%s", + map_copy + (y * world.map.length)); + exit_trouble(test < 0, __func__, "snprintf()"); + write_key_space(file, command); + write_value(file, y); + try_fputc(' ', file, __func__); + write_string(file, string); + try_fputc('\n', file, __func__); + } + free(map_copy); + } +} + + + static void write_thing(FILE * file, struct Thing * t) { struct Thing * o; @@ -145,38 +175,18 @@ static void write_thing(FILE * file, struct Thing * t) { write_key_space_value(file, s[S_CMD_T_CARRIES], o->id); } - if (t->mem_map) + write_mem_map(file, t->mem_depth_map, s[S_CMD_T_MEMDEPTHMAP]); + write_mem_map(file, t->mem_map, s[S_CMD_T_MEMMAP]); + struct ThingInMemory * tm = t->t_mem; + for (; tm; tm = tm->next) { - uint32_t map_size = world.map.length * world.map.length;/* snprintf() */ - char * mem_map_copy = try_malloc(map_size + 1, __func__);/* reads one */ - memcpy(mem_map_copy, t->mem_map, map_size); /* byte beyond map_size */ - mem_map_copy[map_size] = '\0'; /* if string is not \0-terminated. */ - uint16_t y; - char string[UINT8_MAX + 1 + 1]; - for (y = 0; y < world.map.length; y++) - { - - int test = snprintf(string, world.map.length + 1, "%s", - mem_map_copy + (y * world.map.length)); - exit_trouble(test < 0, __func__, "snprintf()"); - write_key_space(file, s[S_CMD_T_MEMMAP]); - write_value(file, y); - try_fputc(' ', file, __func__); - write_string(file, string); - try_fputc('\n', file, __func__); - } - free(mem_map_copy); - struct ThingInMemory * tm = t->t_mem; - for (; tm; tm = tm->next) - { - write_key_space(file, s[S_CMD_T_MEMTHING]); - write_value(file, tm->type); - try_fputc(' ', file, __func__); - write_value(file, tm->pos.y); - try_fputc(' ', file, __func__); - write_value(file, tm->pos.x); - try_fputc('\n', file, __func__); - } + write_key_space(file, s[S_CMD_T_MEMTHING]); + write_value(file, tm->type); + try_fputc(' ', file, __func__); + write_value(file, tm->pos.y); + try_fputc(' ', file, __func__); + write_value(file, tm->pos.x); + try_fputc('\n', file, __func__); } try_fputc('\n', file, __func__); } diff --git a/src/server/thing_actions.c b/src/server/thing_actions.c index dbf600c..31b70af 100644 --- a/src/server/thing_actions.c +++ b/src/server/thing_actions.c @@ -94,6 +94,8 @@ static void actor_hits_actor(struct Thing * hitter, struct Thing * hitted) hitted->fov_map = NULL; free(hitted->mem_map); hitted->mem_map = NULL; + free(hitted->mem_depth_map); + hitted->mem_depth_map = NULL; free_things_in_memory(hitted->t_mem); hitted->t_mem = NULL; } diff --git a/src/server/things.c b/src/server/things.c index d4db193..85503e9 100644 --- a/src/server/things.c +++ b/src/server/things.c @@ -210,6 +210,7 @@ extern void free_things(struct Thing * t) free_things(t->next); free(t->fov_map); free(t->mem_map); + free(t->mem_depth_map); free_things_in_memory(t->t_mem); free(t); if (t == world.things) /* So add_things()' NULL-delimited thing */ diff --git a/src/server/things.h b/src/server/things.h index 03b52d8..cd4040a 100644 --- a/src/server/things.h +++ b/src/server/things.h @@ -25,6 +25,7 @@ struct Thing struct yx_uint8 pos; /* coordinate on map */ char * fov_map; /* thing's FOV map; 'v':visible, 'H':hidden */ char * mem_map; /* map knowledge of thing by FOV and memory */ + char * mem_depth_map; /* map of map memory up-to-dateness */ uint8_t type; /* ID of appropriate thing definition */ uint8_t lifepoints; /* 0: thing is inanimate; >0: hitpoints */ uint8_t command; /* thing's current action; 0 if none */ -- 2.30.2