From 514e381f6e00057fa90c4cdde095daa852eee757 Mon Sep 17 00:00:00 2001
From: PEDERSEN Ny Aina <ny-aina.a.pedersen@inria.fr>
Date: Thu, 19 May 2022 09:14:51 +0200
Subject: [PATCH] Remove old dict entry + new evaluation system

- Condition are now in plain python syntax.
ex: player_x == 5 or player_y == 7
---
 lib/level/level.py  | 92 +++++++++++++++++++++------------------------
 lib/level/test.json | 60 -----------------------------
 lib/level/utils.py  | 12 +++---
 lib/test.c          |  4 +-
 lib/test.json       |  7 ----
 5 files changed, 49 insertions(+), 126 deletions(-)
 delete mode 100644 lib/level/test.json

diff --git a/lib/level/level.py b/lib/level/level.py
index 0d042a44..ba61fb46 100644
--- a/lib/level/level.py
+++ b/lib/level/level.py
@@ -31,7 +31,6 @@ class AbstractLevel(ABC):
         self.internal_commands = ["quit", "q"]
         # Variables and their types
         self.vars = metadata_dict['vars']
-        self.vars_type = metadata_dict['vars_type']
         # Bugs description
         self.bug = metadata_dict['bug']
         # Hints
@@ -123,43 +122,45 @@ class AbstractLevel(ABC):
             return '?'
 
     def update_vars(self):
-        """Update the variables with the correct type."""
-        global_variables = self.tracker.get_global_variables(as_raw_python_objects=True)
+        """
+        Update the variables.
+
+        :return: The list of the variables that have changed.
+        """
+        global_variables = self.tracker.get_global_variables(
+                                            as_raw_python_objects=True)
 
+        updated_vars = []
         for var in self.vars.keys():
             if var in global_variables:
-                self.vars[var] = global_variables[var].value
+                if self.vars[var] != global_variables[var].value:
+                    self.vars[var] = global_variables[var].value
+                    updated_vars.append(var)
+
+        return updated_vars
 
     def available_wowm(self):
         """Return a list of WOWM coordinates."""
-
-        # Variables used by eval
-        exit_x = self.vars['exit_x']
-        exit_y = self.vars['exit_y']
-        player_x = self.vars['player_x']
-        player_y = self.vars['player_y']
-        player_d = self.vars['player_direction']
-
         coordinates = []
         for wowm in self.wowms.values():
-            if not wowm["visible"]:
-                if "always" in wowm["visibility_conditions"]:
-                    wowm["visible"] = True
-                elif "near" in wowm["visibility_conditions"]:
-                    # Near ~= in a 1 tile range
-                    if (abs(player_x - wowm["x"]) <= 1 and
-                        abs(player_y - wowm["y"]) <= 1):
-                        wowm["visible"] = True
+            visible = wowm["visible"]
+            conditions = wowm["visibility_conditions"]
+
+            # Testing the visibility conditions
+            if not visible:
+                if "always" in conditions:
+                    visible = True
+                elif "near" in conditions:
+                    visible = (
+                        abs(self.vars["player_x"] - wowm["x"]) <= 1
+                        and abs(self.vars["player_y"] - wowm["y"]) <= 1
+                    )
                 else:
-                    for condition in wowm["visibility_conditions"]:
-                        ret = eval(condition)
-                        assert(isinstance(ret, bool))
-                        if not ret:
-                            break
-                    else: # All conditions are okay
-                        wowm["visible"] = True
-
-            if wowm["visible"]:
+                    visible = eval(conditions, {}, self.vars) # Custom environment
+
+            # Showing the wowm
+            if visible:
+                wowm["visible"] = True
                 coordinates.append((wowm["x"], wowm["y"]))
 
         return coordinates
@@ -172,34 +173,25 @@ class AbstractLevel(ABC):
         to_delete = []
         message_dict = {}
 
-        # Condition variables (used by eval)
-        exit_x = self.vars['exit_x']
-        exit_y = self.vars['exit_y']
-        player_x = self.vars['player_x']
-        player_y = self.vars['player_y']
-        player_d = self.vars['player_direction']
-
         for wowm_id, wowm in self.wowms.items():
             if wowm["visible"] and not wowm["triggered"]:
+                conditions = wowm["message_conditions"]
                 trigger = False
+
+                # Testing the trigger conditions
                 if "trigger_when_visible" in wowm["labels"]:
                     trigger = True
-                elif "near" in wowm["message_conditions"]:
-                    # Near ~= in a 1 tile range
-                    if (abs(player_x - wowm["x"]) <= 1 and
-                        abs(player_y - wowm["y"]) <= 1):
-                        trigger = True
+                elif "near" in conditions:
+                    trigger = (
+                        abs(self.vars["player_x"] - wowm["x"]) <= 1
+                        and abs(self.vars["player_y"] - wowm["y"]) <= 1
+                    )
                 else:
-                    for condition in wowm["message_conditions"]:
-                        ret = eval(condition)
-                        assert(isinstance(ret, bool))
-                        if not ret:
-                            break
-                    else: # All conditions are okay
-                        trigger = True
+                    trigger = eval(conditions, {}, self.vars) # Custom environment
 
+                # Trigger the wowm
                 if trigger:
-                    wowm["triggered"] = True # Multiple triggers?
+                    wowm["triggered"] = True
                     message_dict[wowm_id] = wowm["message"]
 
                 if "hide_on_trigger" in wowm["labels"]:
@@ -491,7 +483,7 @@ class LevelArcade(AbstractLevel):
 #           self.print_wowm_msg() # TODO implement this
             cmd = self.read_cmd()
             self.parse_and_eval(cmd)
-        
+
         # TODO write this in the GUI
         if self.tracker.get_exit_code() == 0:
             print("VICTORY")
diff --git a/lib/level/test.json b/lib/level/test.json
deleted file mode 100644
index 08994d90..00000000
--- a/lib/level/test.json
+++ /dev/null
@@ -1,60 +0,0 @@
-{
-  "available_commands": [
-    "next",
-    "step",
-    "edit"
-  ],
-  "bug": "here describe the bug",
-  "hints": [
-    "first tip",
-    "second tip"
-  ],
-  "level_name": "test_name",
-  "level_number": 1,
-  "map_height": 7,
-  "map_width": 15,
-  "program_name": "test",
-  "tags": [
-    "#offbyone",
-    "#fixedexit"
-  ],
-  "vars": {
-    "exit_x": 10,
-    "exit_y": 3,
-    "player_direction": 3,
-    "player_x": 3,
-    "player_y": 3
-  },
-  "vars_type": {
-    "exit_x": "int",
-    "exit_y": "int",
-    "player_direction": "int",
-    "player_x": "int",
-    "player_y": "int"
-  },
-  "wowms": {
-    "1": {
-      "labels": [
-        "some_label"
-      ],
-      "message": [
-        "hi, stay still. you are half way to the end!",
-        ":-)"
-      ],
-      "message_conditions": [
-        "player_x==5",
-        "player_y==3",
-        "near"
-      ],
-      "triggered": false,
-      "visibility_conditions": [
-        "player_x==5",
-        "player_y==3",
-        "near"
-      ],
-      "visible": false,
-      "x": 7,
-      "y": 4
-    }
-  }
-}
\ No newline at end of file
diff --git a/lib/level/utils.py b/lib/level/utils.py
index 174917a7..a6ed8fbe 100755
--- a/lib/level/utils.py
+++ b/lib/level/utils.py
@@ -138,8 +138,8 @@ def generate_dict(file):
         # Creation if not existing
         if wowm_index not in wowm_dict:
             wowm_dict[wowm_index] = {
-                "visibility_conditions": [],
-                "message_conditions": [],
+                "visibility_conditions": "",
+                "message_conditions": "",
                 "labels": [],
                 "triggered": False,
                 "visible": False,
@@ -148,16 +148,14 @@ def generate_dict(file):
                 "y": 0,
             }
         # Updating the wowm info
-        if (
-            info.startswith("visibility_conditions ")
-            or info.startswith("labels ")
-            or info.startswith("message_conditions")
-        ):
+        if (info.startswith("labels ")):
             [head, tail] = info.split(" ", 1)
             wowm_dict[wowm_index][head] += str_to_list(tail)
+
         elif info.startswith("message "):
             message = info[len("message ") :]
             wowm_dict[wowm_index]["message"].append(message)
+
         elif info.startswith("position "):
             [_, x, y] = info.split(" ", 2)
             wowm_dict[wowm_index]["x"] = int(x)
diff --git a/lib/test.c b/lib/test.c
index ec5cd2a5..3dc63716 100644
--- a/lib/test.c
+++ b/lib/test.c
@@ -48,8 +48,8 @@ int player_x  = 3,
  * exit_x: 10
  * exit_y: 3
  *
- * WOWM: message_conditions    player_x==5 player_y==3 near
- * WOWM: visibility_conditions player_x==5 player_y==3 near
+ * WOWM: message_conditions    player_x == 5 and player_y == 3
+ * WOWM: visibility_conditions player_x == 5 and player_y == 3
  * WOWM: labels some_label
  * WOWM: message Hi, stay still. You are half way to the end!
  * WOWM: message :-)
diff --git a/lib/test.json b/lib/test.json
index 08994d90..f6f26ae3 100644
--- a/lib/test.json
+++ b/lib/test.json
@@ -25,13 +25,6 @@
     "player_x": 3,
     "player_y": 3
   },
-  "vars_type": {
-    "exit_x": "int",
-    "exit_y": "int",
-    "player_direction": "int",
-    "player_x": "int",
-    "player_y": "int"
-  },
   "wowms": {
     "1": {
       "labels": [
-- 
GitLab