konnektoren_core/achievements/
achievement_evaluator.rs1use super::achievement_definition::{AchievementDefinition, AchievementDefinitions};
2use super::achievement_statistic::*;
3use super::game_statistics::GameStatistics;
4use crate::game::Game;
5use eval::{eval, to_value as eval_to_value};
6
7pub struct AchievementEvaluator {
8 definitions: Vec<AchievementDefinition>,
9}
10impl AchievementEvaluator {
11 pub fn new(yaml_content: &str) -> Result<Self, serde_yaml::Error> {
12 let definitions: AchievementDefinitions = serde_yaml::from_str(yaml_content)?;
13 Ok(AchievementEvaluator {
14 definitions: definitions.achievements,
15 })
16 }
17
18 pub fn evaluate(&self, game: &Game) -> Vec<&AchievementDefinition> {
19 let statistics = GameStatistics::new(game);
20 self.definitions
21 .iter()
22 .filter(|def| self.evaluate_condition(&def.condition, &statistics))
23 .collect()
24 }
25
26 fn evaluate_condition(&self, condition: &str, statistics: &GameStatistics) -> bool {
27 let expression = self.prepare_expression(condition, statistics);
28 let true_value = eval_to_value(true);
29 match eval(&expression) {
30 Ok(value) => value == true_value,
31 Err(_) => false,
32 }
33 }
34
35 fn prepare_expression(&self, condition: &str, statistics: &GameStatistics) -> String {
36 condition
37 .replace(
38 "total_challenges",
39 &statistics.total_challenges().to_string(),
40 )
41 .replace(
42 "average_performance",
43 &statistics.average_performance().to_string(),
44 )
45 .replace("total_xp", &statistics.total_xp().to_string())
46 .replace(
47 "completed_game_paths",
48 &statistics.completed_game_paths().to_string(),
49 )
50 .replace(
51 "perfect_challenges",
52 &statistics.perfect_challenges().to_string(),
53 )
54 .replace(
55 "different_challenge_types_completed",
56 &statistics.different_challenge_types_completed().to_string(),
57 )
58 .replace("&", "&&")
59 .replace("|", "||")
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use crate::challenges::ChallengeHistory;
67 use crate::game::Game;
68
69 const TEST_YAML: &str = r#"
70 achievements:
71 - id: xp_master
72 name: XP Master
73 description: Earn 1000 XP
74 icon: 🏆
75 condition: "total_xp > 1000"
76 - id: challenge_champion
77 name: Challenge Champion
78 description: Complete 50 challenges
79 icon: 🏅
80 condition: "total_challenges >= 50"
81 - id: path_finder
82 name: Path Finder
83 description: Complete 3 game paths
84 icon: 🧭
85 condition: "completed_game_paths >= 3 && perfect_challenges >= 10"
86 "#;
87
88 #[test]
89 fn test_achievement_evaluator() {
90 let evaluator = AchievementEvaluator::new(TEST_YAML).unwrap();
91
92 let mut game = Game::default();
93 game.challenge_history = ChallengeHistory { challenges: vec![] };
94
95 let achieved = evaluator.evaluate(&game);
96
97 assert_eq!(achieved.len(), 0);
98 }
99}