Partially through allowing arguments in actions

continue at makeprompt.h:760
main
Cameron Murphy Reikes 2 years ago
parent db2480a067
commit c1dd916ba9

@ -3,29 +3,38 @@
#include "HandmadeMath.h" #include "HandmadeMath.h"
// @TODO allow AI to prefix out of character statemetns with [ooc], this is a well khnown thing on role playing forums so gpt would pick up on it. // @TODO allow AI to prefix out of character statemetns with [ooc], this is a well khnown thing on role playing forums so gpt would pick up on it.
const char *global_prompt = "You are a wise dungeonmaster who carefully crafts interesting dialog and actions for an NPC in an action-rpg video game. It is critical that you always respond in the format shown below, where you respond like `ACT_action \"This is my response\", even if the player says something vulgar or offensive, as the text is parsed by a program which expects it to look like that. Do not ever refer to yourself as an NPC or show an understanding of the modern world outside the game, always stay in character."; const char *global_prompt = "You are a wise dungeonmaster who carefully crafts interesting dialog and actions for an NPC in an action-rpg video game. It is critical that you always respond in the format shown below, where you respond like `ACT_action \"This is my response\", even if the player says something vulgar or offensive, as the text is parsed by a program which expects it to look like that. Do not ever refer to yourself as an NPC or show an understanding of the modern world outside the game, always stay in character.\n"
"Actions which have () after them take an argument, which somes from some information in the prompt. For example, ACT_give_item() takes an argument, the item to give to the player from the NPC. So the output text looks something like `ACT_give_item(ITEM_sword) \"Here is my sword, young traveler\"`. This item must come from the NPC's inventory which is specified farther down.\n";
const char *top_of_header = "" const char *top_of_header = ""
"#pragma once\n" "#pragma once\n"
"\n"; "\n";
const char *actions[] = { typedef struct
"none", {
const char *name;
bool takes_argument;
} ActionInfo;
ActionInfo actions[] = {
{.name = "none", },
{.name = "give_item", .takes_argument = true, },
// mostly player actions // mostly player actions
"walks_up", {.name = "walks_up", },
"hits_npc", {.name = "hits_npc", },
"leaves", {.name = "leaves", },
// mostly npc actions // mostly npc actions
"allows_player_to_pass", {.name = "allows_player_to_pass", },
"gives_tripod", {.name = "gives_tripod", },
"heals_player", {.name = "heals_player", },
"fights_player", {.name = "fights_player", },
"strikes_air", {.name = "strikes_air", },
"joins_player", {.name = "joins_player", },
"leaves_player", {.name = "leaves_player", },
"stops_fighting_player", {.name = "stops_fighting_player", },
}; };
typedef struct typedef struct

@ -128,7 +128,7 @@ int main(int argc, char **argv)
#define GEN_TABLE(arr_elem_type, table_name, arr, str_access) { fprintf(char_header, "char *%s[] = {\n", table_name); ARR_ITER(arr_elem_type, arr) fprintf(char_header, "\"%s\",\n", str_access); fprintf(char_header, "}; // %s\n", table_name); } #define GEN_TABLE(arr_elem_type, table_name, arr, str_access) { fprintf(char_header, "char *%s[] = {\n", table_name); ARR_ITER(arr_elem_type, arr) fprintf(char_header, "\"%s\",\n", str_access); fprintf(char_header, "}; // %s\n", table_name); }
#define GEN_ENUM(arr_elem_type, arr, enum_type_name, enum_name_access, fmt_str) { fprintf(char_header, "typedef enum\n{\n"); ARR_ITER(arr_elem_type, arr) fprintf(char_header, fmt_str, enum_name_access); fprintf(char_header, "} %s;\n", enum_type_name); GEN_TABLE(arr_elem_type, enum_type_name "_names", arr, enum_name_access); } #define GEN_ENUM(arr_elem_type, arr, enum_type_name, enum_name_access, fmt_str) { fprintf(char_header, "typedef enum\n{\n"); ARR_ITER(arr_elem_type, arr) fprintf(char_header, fmt_str, enum_name_access); fprintf(char_header, "} %s;\n", enum_type_name); GEN_TABLE(arr_elem_type, enum_type_name "_names", arr, enum_name_access); }
GEN_ENUM(char *, actions, "Action", *it, "ACT_%s,\n"); GEN_ENUM(ActionInfo, actions, "Action", it->name, "ACT_%s,\n");
GEN_ENUM(ItemInfo, items, "ItemKind", it->enum_name, "ITEM_%s,\n"); GEN_ENUM(ItemInfo, items, "ItemKind", it->enum_name, "ITEM_%s,\n");
GEN_ENUM(AnimatedSprite, sprites, "AnimKind", it->enum_name, "ANIM_%s,\n"); GEN_ENUM(AnimatedSprite, sprites, "AnimKind", it->enum_name, "ANIM_%s,\n");

@ -1,5 +1,5 @@
// you will die someday // you will die someday
#define CURRENT_VERSION 9 // wehenver you change Entity increment this boz #define CURRENT_VERSION 10 // wehenver you change Entity increment this boz
#define SOKOL_IMPL #define SOKOL_IMPL
#if defined(WIN32) || defined(_WIN32) #if defined(WIN32) || defined(_WIN32)
@ -750,6 +750,8 @@ void reset_level()
if(it->npc_kind == NPC_TheBlacksmith) if(it->npc_kind == NPC_TheBlacksmith)
{ {
BUFF_APPEND(&it->remembered_perceptions, ((Perception){.type = PlayerDialog, .player_dialog = SENTENCE_CONST("Testing dialog")})); BUFF_APPEND(&it->remembered_perceptions, ((Perception){.type = PlayerDialog, .player_dialog = SENTENCE_CONST("Testing dialog")}));
BUFF_APPEND(&it->held_items, ITEM_Tripod);
} }
} }
} }
@ -3165,12 +3167,13 @@ F cost: G + H
#ifdef DESKTOP #ifdef DESKTOP
BUFF(char, 1024) mocked_ai_response = {0}; BUFF(char, 1024) mocked_ai_response = {0};
#define SAY(act, txt) { printf_buff(&mocked_ai_response, "%s \"%s\"", actions[act], txt); } #define SAY(act, txt) { printf_buff(&mocked_ai_response, "%s \"%s\"", actions[act].name, txt); }
#define SAY_ARG(act, txt, arg) { printf_buff(&mocked_ai_response, "%s(" arg ") \"%s\"", actions[act].name, txt); }
if(it->npc_kind == NPC_TheGuard) if(it->npc_kind == NPC_TheGuard)
{ {
if(it->last_seen_holding_kind == ITEM_Tripod && !it->moved) if(it->last_seen_holding_kind == ITEM_Tripod && !it->moved)
{ {
SAY(ACT_allows_player_to_pass, "Here you go"); SAY(ACT_none, "This codepath is deprecated");
} }
else else
{ {
@ -3179,8 +3182,9 @@ F cost: G + H
} }
else else
{ {
SAY_ARG(ACT_give_item, "Here you go" , "ITEM_Tripod");
//SAY(ACT_joins_player, "I am an NPC"); //SAY(ACT_joins_player, "I am an NPC");
SAY(ACT_fights_player, "I am an NPC. Bla bla bl alb djsfklalfkdsaj. Did you know shortcake?"); //SAY(ACT_fights_player, "I am an NPC. Bla bla bl alb djsfklalfkdsaj. Did you know shortcake?");
} }
Perception p = {0}; Perception p = {0};
assert(parse_chatgpt_response(it, mocked_ai_response.data, &p)); assert(parse_chatgpt_response(it, mocked_ai_response.data, &p));

@ -87,6 +87,9 @@ typedef struct Perception
struct struct
{ {
Action player_action_type; Action player_action_type;
// only valid when giving item action
ItemKind given_item;
}; };
// player dialog // player dialog
@ -499,13 +502,13 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
if(it->type == PlayerAction) if(it->type == PlayerAction)
{ {
assert(it->player_action_type < ARRLEN(actions)); assert(it->player_action_type < ARRLEN(actions));
printf_buff(&cur_node, "Player: ACT_%s", actions[it->player_action_type]); printf_buff(&cur_node, "Player: ACT_%s", actions[it->player_action_type].name);
dump_json_node(into, MSG_USER, cur_node.data); dump_json_node(into, MSG_USER, cur_node.data);
} }
else if(it->type == EnemyAction) else if(it->type == EnemyAction)
{ {
assert(it->enemy_action_type < ARRLEN(actions)); assert(it->enemy_action_type < ARRLEN(actions));
printf_buff(&cur_node, "An Enemy: ACT_%s", actions[it->player_action_type]); printf_buff(&cur_node, "An Enemy: ACT_%s", actions[it->player_action_type].name);
dump_json_node(into, MSG_USER, cur_node.data); dump_json_node(into, MSG_USER, cur_node.data);
} }
else if(it->type == PlayerDialog) else if(it->type == PlayerDialog)
@ -534,7 +537,7 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
else if(it->type == NPCDialog) else if(it->type == NPCDialog)
{ {
assert(it->npc_action_type < ARRLEN(actions)); assert(it->npc_action_type < ARRLEN(actions));
printf_buff(&cur_node, "%s: ACT_%s \"%s\"", characters[e->npc_kind].name, actions[it->npc_action_type], it->npc_dialog.data); printf_buff(&cur_node, "%s: ACT_%s \"%s\"", characters[e->npc_kind].name, actions[it->npc_action_type].name, it->npc_dialog.data);
dump_json_node(into, MSG_ASSISTANT, cur_node.data); dump_json_node(into, MSG_ASSISTANT, cur_node.data);
} }
else if(it->type == PlayerHeldItemChanged) else if(it->type == PlayerHeldItemChanged)
@ -587,11 +590,11 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
{ {
if(i == available.cur_index - 1) if(i == available.cur_index - 1)
{ {
printf_buff(&latest_state_node, "ACT_%s", actions[*it]); printf_buff(&latest_state_node, "ACT_%s", actions[*it].name);
} }
else else
{ {
printf_buff(&latest_state_node, "ACT_%s, ", actions[*it]); printf_buff(&latest_state_node, "ACT_%s, ", actions[*it].name);
} }
} }
printf_buff(&latest_state_node, "]"); printf_buff(&latest_state_node, "]");
@ -606,6 +609,7 @@ void generate_chatgpt_prompt(Entity *it, PromptBuff *into)
printf_buff(into, "]"); printf_buff(into, "]");
} }
/*
void generate_prompt(Entity *it, PromptBuff *into) void generate_prompt(Entity *it, PromptBuff *into)
{ {
assert(it->is_npc); assert(it->is_npc);
@ -709,6 +713,7 @@ void generate_prompt(Entity *it, PromptBuff *into)
printf_buff(into, "The NPC, %s: ACT_INDEX", characters[e->npc_kind].name); printf_buff(into, "The NPC, %s: ACT_INDEX", characters[e->npc_kind].name);
} }
*/
bool parse_chatgpt_response(Entity *it, char *sentence_str, Perception *out) bool parse_chatgpt_response(Entity *it, char *sentence_str, Perception *out)
{ {
@ -717,22 +722,21 @@ bool parse_chatgpt_response(Entity *it, char *sentence_str, Perception *out)
size_t sentence_length = strlen(sentence_str); size_t sentence_length = strlen(sentence_str);
char action_string[512] = {0}; BUFF(char, 512) action_string = {0};
char dialog_string[512] = {0}; int i = 0;
int variables_filled = sscanf(sentence_str, "%511s \"%511[^\n]\"", action_string, dialog_string); while(sentence_str[i] != '(' && sentence_str[i] != ' ' && BUFF_HAS_SPACE(&action_string))
if(strlen(action_string) == 0 || strlen(dialog_string) == 0 || variables_filled != 2)
{ {
Log("sscanf failed to parse chatgpt string `%s`, variables unfilled. Action string: `%s` dialog string `%s`\n", sentence_str, action_string, dialog_string); BUFF_APPEND(&action_string, sentence_str[i]);
return false; i++;
} }
sentence_str += i;
AvailableActions available = {0}; AvailableActions available = {0};
fill_available_actions(it, &available); fill_available_actions(it, &available);
bool found_action = false; bool found_action = false;
BUFF_ITER(Action, &available) BUFF_ITER(Action, &available)
{ {
if(strcmp(actions[*it], action_string) == 0) if(strcmp(actions[*it].name, action_string) == 0)
{ {
found_action = true; found_action = true;
out->npc_action_type = *it; out->npc_action_type = *it;
@ -744,6 +748,24 @@ bool parse_chatgpt_response(Entity *it, char *sentence_str, Perception *out)
out->npc_action_type = ACT_none; out->npc_action_type = ACT_none;
} }
char dialog_string[512] = {0};
if(actions[it->npc_action_type].takes_argument)
{
char argument_string[512] = {0};
int filled = sscanf(sentence_str, "(%511s) \"511[^\n]\"", argument_string, dialog_string);
if(strlen(action_string) == 0 || strlen(dialog_string) == 0 || variables_filled != 2)
{
Log("sscanf failed to parse chatgpt string `%s`, variables unfilled. Action string: `%s` dialog string `%s` argument string `%s`\n", sentence_str, action_string, dialog_string, argument_string);
return false;
}
}
char action_string[512] = {0};
int variables_filled = sscanf(sentence_str, "%511s \"%511[^\n]\"", action_string, dialog_string);
if(strlen(dialog_string) >= ARRLEN(out->npc_dialog.data)) if(strlen(dialog_string) >= ARRLEN(out->npc_dialog.data))
{ {
Log("Dialog string `%s` too big to fit in sentence size %d\n", dialog_string, (int)ARRLEN(out->npc_dialog.data)); Log("Dialog string `%s` too big to fit in sentence size %d\n", dialog_string, (int)ARRLEN(out->npc_dialog.data));

Loading…
Cancel
Save