diff --git a/game/src/ai/prompts/execution_prompts.rs b/game/src/ai/prompts/execution_prompts.rs index b35427f..45c4cfa 100644 --- a/game/src/ai/prompts/execution_prompts.rs +++ b/game/src/ai/prompts/execution_prompts.rs @@ -7,6 +7,7 @@ use crate::models::world::items::Item; use crate::models::world::people::Person; use crate::models::world::scenes::{Exit, Prop, Scene, Stage}; use crate::models::Insertable; +use gbnf::prelude::limit::{GbnfLimitedComplex, GbnfLimitedPrimitive}; use itertools::Itertools; use strum::VariantNames; use tabled::settings::Style; @@ -75,19 +76,6 @@ impl<'a> From<&'a Exit> for ExitTableRow<'a> { } } -const COMMAND_EXECUTION_BNF: &'static str = r#" -root ::= CommandExecution -CommandEvent ::= "{" ws "\"eventName\":" ws string "," ws "\"appliesTo\":" ws string "," ws "\"parameter\":" ws string "}" -CommandExecution ::= "{" ws "\"valid\":" ws boolean "," ws "\"reason\":" ws string "," ws "\"narration\":" ws string "," ws "\"event\":" ws CommandEvent "}" -CommandExecutionlist ::= "[]" | "[" ws CommandExecution ("," ws CommandExecution)* "]" -string ::= "\"" ([^"]*) "\"" -boolean ::= "true" | "false" -ws ::= [ \t\n]* -number ::= [0-9]+ "."? [0-9]* -stringlist ::= "[" ws "]" | "[" ws string ("," ws string)* ws "]" -numberlist ::= "[" ws "]" | "[" ws string ("," ws number)* ws "]" -"#; - const COMMAND_EXECUTION_PROMPT: &'static str = r#" [INST] You are running a text-based adventure game. You have been given a command to execute. Your response must be in JSON. @@ -248,41 +236,25 @@ fn stage_info(stage: &Stage) -> String { } fn execution_gbnf_limit<'a>(stage: &'a Stage) -> RawCommandExecutionGbnfLimit { - // We will begin this implementation by simply setting the limits + // First version of this implementation is simply setting the limits // to all uuids in the scene. We might wind up getting rid of the // appliesTo field, leaving only parameter behind. - let people_keys = stage - .people - .iter() - .map(|p| &p._key) - .cloned() - .flatten(); - - let item_keys = stage - .items - .iter() - .map(|i| &i._key) - .cloned() - .flatten(); - - let curr_scene_key = stage.scene._key.iter(); + let people_keys = stage.people.iter().map(|p| &p._key).cloned().flatten(); + let item_keys = stage.items.iter().map(|i| &i._key).cloned().flatten(); let exit_keys = stage.scene.exits.iter().map(|e| &e.scene_key).cloned(); - let all_uuids = people_keys - .chain(item_keys) - //.chain(curr_scene_key) - .chain(exit_keys) - .collect_vec(); + let all_uuids = people_keys.chain(item_keys).chain(exit_keys).collect_vec(); + let mut applies_to = all_uuids.clone(); + applies_to.push("self".to_string()); - //let all_uuids = std::rc::Rc::new(all_uuids); let event_limit = RawCommandEventGbnfLimit { - applies_to: Box::new(all_uuids.clone()), - parameter: Box::new(all_uuids.clone()), + applies_to: GbnfLimitedPrimitive::new(applies_to), + parameter: GbnfLimitedPrimitive::new(all_uuids), }; RawCommandExecutionGbnfLimit { - event: Box::new(event_limit), + event: GbnfLimitedComplex::new(event_limit), } } @@ -299,9 +271,6 @@ pub fn execution_prompt(original_cmd: &str, stage: &Stage, cmd: &ParsedCommand) let limit = execution_gbnf_limit(stage); let grammar = RawCommandExecution::to_grammar_with_limit(limit); - println!("{}", grammar); - - //AiPrompt::new_with_grammar_and_size(&prompt, COMMAND_EXECUTION_BNF, 512) AiPrompt::new_with_grammar_and_size(&prompt, &grammar, 512) } diff --git a/game/src/main.rs b/game/src/main.rs index d34952f..165b04a 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -2,7 +2,10 @@ use ai::logic::AiLogic; use anyhow::Result; use config::Config; use game_loop::GameLoop; -use models::world::scenes::{root_scene_id, Stage}; +use models::{ + commands::RawCommandExecutionGbnfLimit, + world::scenes::{root_scene_id, Stage}, +}; use state::GameState; use std::{io::stdout, rc::Rc, str::FromStr, time::Duration}; diff --git a/game/src/models/commands.rs b/game/src/models/commands.rs index 12db19b..90d08ef 100644 --- a/game/src/models/commands.rs +++ b/game/src/models/commands.rs @@ -1,7 +1,5 @@ -use std::fmt::Display; - use gbnf::prelude::*; -use gbnf_derive::{Gbnf}; +use gbnf_derive::Gbnf; use serde::{Deserialize, Serialize}; use strum::{EnumString, EnumVariantNames}; use thiserror::Error; @@ -63,7 +61,7 @@ pub struct RawCommandExecution { pub reason: Option, pub narration: String, #[serde(skip_serializing_if = "Option::is_none")] - #[gbnf_limit] + #[gbnf_limit_complex] pub event: Option, } @@ -82,9 +80,9 @@ impl RawCommandExecution { #[serde(rename_all = "snake_case")] pub struct RawCommandEvent { pub event_name: String, - #[gbnf_limit] + #[gbnf_limit_primitive] pub applies_to: String, - #[gbnf_limit] + #[gbnf_limit_primitive] pub parameter: String, } diff --git a/gbnf/src/lib.rs b/gbnf/src/lib.rs index e782f0b..f8ae977 100644 --- a/gbnf/src/lib.rs +++ b/gbnf/src/lib.rs @@ -4,24 +4,31 @@ use std::{ ops::{Deref, DerefMut}, }; -use itertools::Itertools; -use limited::{Limited, LimitedGbnfComplex, LimitedGbnfField, LimitedGbnfPrimitive}; -use serde::de::DeserializeOwned; +use crate::limits::convert::{Limited, LimitedGbnfComplex, LimitedGbnfField, LimitedGbnfPrimitive}; use convert_case::{Case, Casing}; +use itertools::Itertools; +use serde::de::DeserializeOwned; -mod limited; +mod limits; + +pub mod limit { + pub use crate::limits::definitions::*; +} pub mod prelude { pub use crate::gbnf_field; pub use crate::gbnf_field_type; + pub use crate::limit; + pub use crate::limit::*; pub use crate::AsGbnf; + pub use crate::AsGbnfComplex; + pub use crate::AsGbnfLimit; pub use crate::AsGbnfPrimitive; pub use crate::AsGrammar; pub use crate::GbnfComplex; pub use crate::GbnfField; pub use crate::GbnfFieldType; pub use crate::GbnfLimit; - pub use crate::GbnfLimitedField; pub use crate::GbnfPrimitive; pub use crate::GbnfRule; pub use crate::GbnfRuleLevel; @@ -35,106 +42,8 @@ pub mod prelude { /// complex types). This allows the creation of a complex nested Limit /// struct which can hold the full structure of nested limits of /// multiple types that derive Gbnf and have limits. -pub trait GbnfLimitedField { - fn limit(self: Box) -> GbnfLimit; -} - -impl + 'static> From> for Box> { - fn from(value: Box) -> Self { - value - } -} - -impl From> for Box> { - fn from(value: Vec) -> Self { - Box::new(value) - } -} - -impl From<[T; N]> for Box> -where - T: ToString + AsGbnfPrimitive + 'static, -{ - fn from(value: [T; N]) -> Self { - Box::new(value) - } -} - -impl From>> for Box> { - fn from(value: Box>) -> Self { - value - } -} - -// Single pritive field types that can only have specific values. -impl GbnfLimitedField for Vec { - fn limit(self: Box) -> GbnfLimit { - GbnfLimit::Simple( - T::to_gbnf_primitive(), - self.into_iter().map(|v| v.to_string()).collect(), - ) - } -} - -impl GbnfLimitedField> for Vec { - fn limit(self: Box) -> GbnfLimit { - GbnfLimit::Simple( - T::to_gbnf_primitive(), - self.into_iter().map(|v| v.to_string()).collect(), - ) - } -} - -// List types that can only have specific values. -impl GbnfLimitedField> for Vec { - fn limit(self: Box) -> GbnfLimit { - GbnfLimit::Simple( - T::to_gbnf_primitive(), - self.into_iter().map(|v| v.to_string()).collect(), - ) - } -} - -impl GbnfLimitedField for [T; N] -where - T: ToString + AsGbnfPrimitive, -{ - fn limit(self: Box) -> GbnfLimit { - GbnfLimit::Simple( - T::to_gbnf_primitive(), - self.into_iter().map(|v| v.to_string()).collect(), - ) - } -} - -impl GbnfLimitedField> for [T; N] -where - T: ToString + AsGbnfPrimitive, -{ - fn limit(self: Box) -> GbnfLimit { - GbnfLimit::Simple( - T::to_gbnf_primitive(), - self.into_iter().map(|v| v.to_string()).collect(), - ) - } -} - -impl<'a> GbnfLimitedField for &'a [&'a str] { - fn limit(self: Box) -> GbnfLimit { - GbnfLimit::Simple( - String::to_gbnf_primitive(), - self.into_iter().map(|v| v.to_string()).collect(), - ) - } -} - -impl<'a> GbnfLimitedField for std::rc::Rc> { - fn limit(self: Box) -> GbnfLimit { - GbnfLimit::Simple( - String::to_gbnf_primitive(), - self.iter().map(|v| v.to_string()).collect(), - ) - } +pub trait AsGbnfLimit { + fn to_gbnf_limit(self) -> GbnfLimit; } /// A type of GBNF value limit, either simple (a list of values meant @@ -147,12 +56,6 @@ pub enum GbnfLimit { Complex(HashMap<&'static str, GbnfLimit>), } -impl From>> for GbnfLimit { - fn from(value: Box>) -> Self { - value.limit() - } -} - // Converts GBNF defintions (through the types below) into the grammar // rules. pub trait AsGrammar { @@ -180,6 +83,10 @@ pub trait AsGbnfPrimitive { fn to_gbnf_primitive() -> GbnfPrimitive; } +pub trait AsGbnfComplex { + fn to_gbnf_complex() -> GbnfComplex; +} + macro_rules! define_field_type { ($type:ty, $gbnf_type:expr) => { impl AsGbnf for $type { @@ -331,6 +238,8 @@ impl GbnfRule { ) } + /// Turn this rule into an option rule: main rule is an option type, and + /// dependent rule is the original rule itself. pub fn to_optional(self) -> GbnfRule { let option_token = self.name.option_token(); let option_rule_text = format!(r#"{} | "null""#, self.name); @@ -340,7 +249,7 @@ impl GbnfRule { } /// Turn this rule into a list rule: main rule is a list type, and - /// dependent rule is the original type/rule itself. + /// dependent rule is the original rule itself. pub fn to_list(self) -> GbnfRule { let list_name = self.name.list_token(); let list_rule_text = @@ -354,7 +263,7 @@ impl GbnfRule { list_rule } - /// Consume this rule to produce a list of rules consisting of + /// Consume this rule to produce an ordered list of rules consisting of /// this rule and its dependents. The rules in the list have no /// dependents. Useful for final output of a rules list. fn flatten(mut self) -> Vec { diff --git a/gbnf/src/limited.rs b/gbnf/src/limits/convert.rs similarity index 99% rename from gbnf/src/limited.rs rename to gbnf/src/limits/convert.rs index 7557f56..ae79acd 100644 --- a/gbnf/src/limited.rs +++ b/gbnf/src/limits/convert.rs @@ -4,6 +4,7 @@ use crate::{ }; use itertools::Itertools; + #[derive(Debug, Clone)] pub struct LimitedGbnfComplex<'a> { pub complex: &'a GbnfComplex, diff --git a/gbnf/src/limits/definitions.rs b/gbnf/src/limits/definitions.rs new file mode 100644 index 0000000..2eb6696 --- /dev/null +++ b/gbnf/src/limits/definitions.rs @@ -0,0 +1,157 @@ +use crate::AsGbnfComplex; +use crate::AsGbnfLimit; +use crate::AsGbnfPrimitive; +use crate::GbnfLimit; + +pub trait GbnfLimitType { + type Type; +} + +pub trait GbnfLimitStructMarker {} + +macro_rules! define_limit_marker { + ($field_type:ty, $value_type:ty) => { + impl GbnfLimitType for $field_type { + type Type = $value_type; + } + }; +} + +define_limit_marker!(String, String); +define_limit_marker!(i32, i32); + +impl GbnfLimitType for Vec +where + T: GbnfLimitType, +{ + type Type = ::Type; +} + +impl GbnfLimitType for Option { + type Type = T; +} + +pub trait GbnfLimitTypeContainer { + type ContainerType; +} + +impl GbnfLimitTypeContainer for String { + type ContainerType = Vec; +} + +impl GbnfLimitTypeContainer for i32 { + type ContainerType = Vec; +} + +impl GbnfLimitTypeContainer for Vec { + type ContainerType = Vec<::Type>; +} + +impl> GbnfLimitTypeContainer> for Vec { + type ContainerType = >::ContainerType; +} + +impl GbnfLimitTypeContainer for Option { + type ContainerType = ::Type; +} + +impl GbnfLimitTypeContainer> for Option { + type ContainerType = ::Type; +} + +pub struct GbnfLimitedPrimitive +where + T: GbnfLimitType + GbnfLimitTypeContainer, +{ + field_type: std::marker::PhantomData, + values: >::ContainerType, +} + +impl GbnfLimitedPrimitive +where + T: GbnfLimitType + GbnfLimitTypeContainer, +{ + pub fn new(values: >::ContainerType) -> GbnfLimitedPrimitive { + GbnfLimitedPrimitive { + field_type: std::marker::PhantomData, + values, + } + } +} + +impl AsGbnfLimit for GbnfLimitedPrimitive +where + T: AsGbnfPrimitive + + ToString + + GbnfLimitTypeContainer> + + GbnfLimitType, +{ + fn to_gbnf_limit(self) -> GbnfLimit { + let vals = self.values.into_iter().map(|v| v.to_string()); + GbnfLimit::Simple(::to_gbnf_primitive(), vals.collect()) + } +} + +impl AsGbnfLimit for GbnfLimitedPrimitive> +where + T: AsGbnfPrimitive + + ToString + + GbnfLimitTypeContainer> + + GbnfLimitType, + Vec: GbnfLimitType, +{ + fn to_gbnf_limit(self) -> GbnfLimit { + let vals = self.values.into_iter().map(|v| v.to_string()); + GbnfLimit::Simple(::to_gbnf_primitive(), vals.collect()) + } +} + +pub struct GbnfLimitedComplex +where + T: GbnfLimitType + GbnfLimitTypeContainer, +{ + field_type: std::marker::PhantomData, + values: >::ContainerType, +} + +impl GbnfLimitedComplex +where + T: GbnfLimitType + GbnfLimitTypeContainer, +{ + pub fn new(values: >::ContainerType) -> GbnfLimitedComplex { + GbnfLimitedComplex { + field_type: std::marker::PhantomData, + values, + } + } +} + +impl AsGbnfLimit for GbnfLimitedComplex +where + T: AsGbnfComplex + GbnfLimitType + GbnfLimitTypeContainer, + U: AsGbnfLimit + GbnfLimitStructMarker, +{ + fn to_gbnf_limit(self) -> GbnfLimit { + self.values.to_gbnf_limit() + } +} + +impl AsGbnfLimit for GbnfLimitedComplex> +where + T: AsGbnfComplex + GbnfLimitType + GbnfLimitTypeContainer, + U: AsGbnfLimit + GbnfLimitStructMarker, +{ + fn to_gbnf_limit(self) -> GbnfLimit { + self.values.to_gbnf_limit() + } +} + +impl AsGbnfLimit for GbnfLimitedComplex> +where + T: GbnfLimitType + GbnfLimitTypeContainer, + U: AsGbnfLimit + GbnfLimitStructMarker, +{ + fn to_gbnf_limit(self) -> GbnfLimit { + self.values.to_gbnf_limit() + } +} diff --git a/gbnf/src/limits/mod.rs b/gbnf/src/limits/mod.rs new file mode 100644 index 0000000..0b15afa --- /dev/null +++ b/gbnf/src/limits/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod convert; +pub mod definitions; diff --git a/gbnf_derive/src/lib.rs b/gbnf_derive/src/lib.rs index 8ba3573..9cbdc4f 100644 --- a/gbnf_derive/src/lib.rs +++ b/gbnf_derive/src/lib.rs @@ -3,11 +3,11 @@ use darling::{FromDeriveInput, FromField}; use proc_macro::{Span, TokenStream}; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote, ToTokens}; -use syn::parse_macro_input; +use syn::{parse_macro_input, Attribute}; use syn::{DeriveInput, Ident, LitStr}; #[derive(Clone, Debug, FromField)] -#[darling(forward_attrs(gbnf_limit))] +#[darling(forward_attrs(gbnf_limit_primitive, gbnf_limit_complex))] struct GbnfFieldDef { ident: Option, ty: syn::Type, @@ -15,14 +15,47 @@ struct GbnfFieldDef { attrs: Vec, } +enum LimitType { + LimitedPrimitive, + LimitedComplex, + NotLimited, +} + +impl GbnfFieldDef { + fn limit_type(&self) -> LimitType { + let limit_priv = self + .attrs + .iter() + .find(|attr| attr.path().get_ident().unwrap().to_string() == "gbnf_limit_primitive"); + + let limit_complex = self + .attrs + .iter() + .find(|attr| attr.path().get_ident().unwrap().to_string() == "gbnf_limit_complex"); + + match (limit_priv, limit_complex) { + (Some(_), None) => LimitType::LimitedPrimitive, + (None, Some(_)) => LimitType::LimitedComplex, + (Some(_), Some(_)) => panic!("cannot be both a primitive and complex limit"), + (None, None) => LimitType::NotLimited, + } + } +} + impl ToTokens for GbnfFieldDef { fn to_tokens(&self, tokens: &mut TokenStream2) { let ident = &self.ident; let ty = &self.ty; // TODO figure out how to get ident out let vis = &self.vis; + let wrapper_type = match self.limit_type() { + LimitType::LimitedPrimitive => quote! { GbnfLimitedPrimitive<#ty> }, + LimitType::LimitedComplex => quote! { GbnfLimitedComplex<#ty> }, + _ => panic!("somehow attempting to make a limited field without a helper attr"), + }; + let output = quote! { - #vis #ident: Box> + #vis #ident: #wrapper_type }; output.to_tokens(tokens); @@ -55,13 +88,18 @@ impl ToTokens for GbnfStructDef { } } +fn is_limited_field(attr: &Attribute) -> bool { + let ident = attr.path().get_ident().unwrap().to_string(); + ident == "gbnf_limit_primitive" || ident == "gbnf_limit_complex" +} + /// Find fields in the struct with a #[gbnf_limit] attribute. fn find_limited_fields(fields: &[GbnfFieldDef]) -> impl Iterator + '_ { fields.iter().filter(|field| { field .attrs .iter() - .find(|attr| attr.path().get_ident().unwrap().to_string() == "gbnf_limit") + .find(|attr| is_limited_field(attr)) .is_some() }) } @@ -72,7 +110,7 @@ fn find_non_limited_fields(fields: &[GbnfFieldDef]) -> impl Iterator GbnfComplex { + Self::to_gbnf().as_complex() + } + } + }; + if limit_struct_fields.len() > 0 { quote! { pub struct #limit_struct_name { #(#limit_struct_fields),* } - impl #limit_struct_name { - pub fn to_gbnf_limit(self) -> GbnfLimit { + impl GbnfLimitStructMarker for #limit_struct_name {} + + impl AsGbnfLimit for #limit_struct_name { + fn to_gbnf_limit(self) -> GbnfLimit { GbnfLimit::Complex( HashMap::from([ #(#from_assignments),* @@ -115,28 +163,12 @@ fn generate_to_grammar_impl(original_struct_name: &Ident, fields: &[GbnfFieldDef } } - impl GbnfLimitedField<#original_struct_name> for #limit_struct_name { - fn limit(self: Box<#limit_struct_name>) -> GbnfLimit { - self.to_gbnf_limit() - } + impl GbnfLimitType for #original_struct_name { + type Type = #limit_struct_name; } - impl GbnfLimitedField> for #limit_struct_name { - fn limit(self: Box<#limit_struct_name>) -> GbnfLimit { - self.to_gbnf_limit() - } - } - - impl From<#limit_struct_name> for Box> { - fn from(limit: #limit_struct_name) -> Self { - Box::new(limit) - } - } - - impl From<#limit_struct_name> for Box>> { - fn from(limit: #limit_struct_name) -> Self { - Box::new(limit) - } + impl GbnfLimitTypeContainer<#original_struct_name> for #original_struct_name { + type ContainerType = #limit_struct_name; } impl #original_struct_name { @@ -145,6 +177,8 @@ fn generate_to_grammar_impl(original_struct_name: &Ident, fields: &[GbnfFieldDef Self::to_gbnf().as_complex().to_grammar(Some(gbnf_limit)) } } + + #as_gbnf_complex_impl } } else { quote! { @@ -155,6 +189,8 @@ fn generate_to_grammar_impl(original_struct_name: &Ident, fields: &[GbnfFieldDef GRAMMAR.get_or_init(|| Self::to_gbnf().as_complex().to_grammar(None)) } } + + #as_gbnf_complex_impl } } } @@ -241,7 +277,7 @@ fn map_limited_gbnfs(fields: &[GbnfFieldDef]) -> impl Iterator TokenStream { let input = parse_macro_input!(input as DeriveInput); generate_gbnf(&input)