home · contact · privacy
Better integration of eat cost vs. eat gain in all AI deliberations.
[plomrogue] / roguelike-server
index 959d769ea899e0eb6cfbae6315cc7af0f9d6df5e..4ace6e9d9bd621b2ea51b05eb3d74e5ff6cbfadf 100755 (executable)
@@ -99,6 +99,11 @@ def cleanup_server_io():
         io_db["file_record"].close()
 
 
+def log(msg):
+    """Send "msg" to log."""
+    strong_write(io_db["file_out"], "LOG " + msg + "\n")
+
+
 def obey(command, prefix, replay=False, do_record=False):
     """Call function from commands_db mapped to command's first token.
 
@@ -308,21 +313,6 @@ def try_worldstate_update():
     """Write worldstate file if io_db["worldstate_updateable"] is set."""
     if io_db["worldstate_updateable"]:
 
-        def draw_visible_Things(map, run):
-            for id in world_db["Things"]:
-                type = world_db["Things"][id]["T_TYPE"]
-                consumable = world_db["ThingTypes"][type]["TT_TOOL"] == "food"
-                alive = world_db["ThingTypes"][type]["TT_LIFEPOINTS"]
-                if (0 == run and not consumable and not alive) \
-                   or (1 == run and consumable and not alive) \
-                   or (2 == run and alive):
-                    y = world_db["Things"][id]["T_POSY"]
-                    x = world_db["Things"][id]["T_POSX"]
-                    fovflag = world_db["Things"][0]["fovmap"][(y * length) + x]
-                    if 'v' == chr(fovflag):
-                        c = world_db["ThingTypes"][type]["TT_SYMBOL"]
-                        map[(y * length) + x] = ord(c)
-
         def write_map(string, map):
             for i in range(length):
                 line = map[i * length:(i * length) + length].decode()
@@ -348,21 +338,79 @@ def try_worldstate_update():
                  str(world_db["Things"][0]["T_POSX"]) + "\n" + \
                  str(world_db["MAP_LENGTH"]) + "\n"
         length = world_db["MAP_LENGTH"]
+
         fov = bytearray(b' ' * (length ** 2))
-        for pos in range(length ** 2):
-            if 'v' == chr(world_db["Things"][0]["fovmap"][pos]):
-                fov[pos] = world_db["MAP"][pos]
-        for i in range(3):
-            draw_visible_Things(fov, i)
+        ord_v = ord("v")
+        for pos in [pos for pos in range(length ** 2)
+                        if ord_v == world_db["Things"][0]["fovmap"][pos]]:
+            fov[pos] = world_db["MAP"][pos]
+        for id in [id for tid in reversed(sorted(list(world_db["ThingTypes"])))
+                      for id in world_db["Things"]
+                      if not world_db["Things"][id]["carried"]
+                      if world_db["Things"][id]["T_TYPE"] == tid
+                      if world_db["Things"][0]["fovmap"][
+                           world_db["Things"][id]["T_POSY"] * length
+                           + world_db["Things"][id]["T_POSX"]] == ord_v]:
+            type = world_db["Things"][id]["T_TYPE"]
+            c = ord(world_db["ThingTypes"][type]["TT_SYMBOL"])
+            fov[world_db["Things"][id]["T_POSY"] * length
+                + world_db["Things"][id]["T_POSX"]] = c
         string = write_map(string, fov)
+
         mem = world_db["Things"][0]["T_MEMMAP"][:]
-        for i in range(2):
-            for mt in world_db["Things"][0]["T_MEMTHING"]:
-                consumable = world_db["ThingTypes"][mt[0]]["TT_TOOL"] == "food"
-                if (i == 0 and not consumable) or (i == 1 and consumable):
-                    c = world_db["ThingTypes"][mt[0]]["TT_SYMBOL"]
-                    mem[(mt[1] * length) + mt[2]] = ord(c)
+        for mt in [mt for tid in reversed(sorted(list(world_db["ThingTypes"])))
+                      for mt in world_db["Things"][0]["T_MEMTHING"]
+                      if mt[0] == tid]:
+             c = world_db["ThingTypes"][mt[0]]["TT_SYMBOL"]
+             mem[(mt[1] * length) + mt[2]] = ord(c)
         string = write_map(string, mem)
+
+        metamapA = bytearray(b'0' * (length ** 2))  # #
+        for id in [id for id in world_db["Things"]  # #
+                      if not world_db["Things"][id]["carried"]  # #
+                      if world_db["Things"][id]["T_LIFEPOINTS"]  # #
+                      if world_db["Things"][0]["fovmap"][  # #
+                           world_db["Things"][id]["T_POSY"] * length  # #
+                           + world_db["Things"][id]["T_POSX"]] == ord_v]:  # #
+            pos = (world_db["Things"][id]["T_POSY"] * length  # #
+                  + world_db["Things"][id]["T_POSX"])  # #
+            if id == 0 or world_db["EMPATHY"]:  # #
+                type = world_db["Things"][id]["T_TYPE"]  # #
+                max_hp = world_db["ThingTypes"][type]["TT_LIFEPOINTS"]  # #
+                third_of_hp = max_hp / 3  # #
+                hp = world_db["Things"][id]["T_LIFEPOINTS"]  # #
+                add = 0  # #
+                if hp > 2 * third_of_hp:  # #
+                     add = 2  # #
+                elif hp > third_of_hp:  # #
+                    add = 1  # #
+                metamapA[pos] = ord('a') + add # #
+            else:  # #
+                metamapA[pos] = ord('X')  # #
+        for mt in world_db["Things"][0]["T_MEMTHING"]:  # #
+            pos = mt[1] * length + mt[2]  # #
+            if metamapA[pos] < ord('2'):  # #
+                metamapA[pos] += 1  # #
+        string = write_map(string, metamapA)  # #
+        
+        metamapB = bytearray(b' ' * (length ** 2))  # #
+        for id in [id for id in world_db["Things"]  # #
+                      if not world_db["Things"][id]["carried"]  # #
+                      if world_db["Things"][id]["T_LIFEPOINTS"]  # #
+                      if world_db["Things"][0]["fovmap"][  # #
+                           world_db["Things"][id]["T_POSY"] * length  # #
+                           + world_db["Things"][id]["T_POSX"]] == ord_v]:  # #
+            pos = (world_db["Things"][id]["T_POSY"] * length  # #
+                  + world_db["Things"][id]["T_POSX"])  # #
+            if id == 0 or world_db["EMPATHY"]:  # #
+                action = world_db["Things"][id]["T_COMMAND"]  # #
+                if 0 != action:  # #
+                    name = world_db["ThingActions"][action]["TA_NAME"]  # #
+                else:  # #
+                    name = " "  # #
+                metamapB[pos] = ord(name[0]) # #
+        string = write_map(string, metamapB)  # #
+
         atomic_write(io_db["path_worldstate"], string, delete=False)
         strong_write(io_db["file_out"], "WORLD_UPDATED\n")
         io_db["worldstate_updateable"] = False
@@ -501,6 +549,14 @@ def make_map():
             altar_placed = True  # #
 
 
+def eat_vs_hunger_threshold(thingtype):
+    """Return satiation cost of eating for type. Good food for it must be >."""
+    hunger_unit = hunger_per_turn(thingtype)
+    actiontype = [id for id in world_db["ThingActions"]
+               if world_db["ThingActions"][id]["TA_NAME"] == "use"][0]
+    return world_db["ThingActions"][actiontype]["TA_EFFORT"] * hunger_unit
+
+
 def update_map_memory(t, age_map=True):
     """Update t's T_MEMMAP with what's in its FOV now,age its T_MEMMEPTHMAP."""
 
@@ -607,20 +663,14 @@ def build_fov_map(t):
 
 def log_help():
     """Send quick usage info to log."""
-    strong_write(io_db["file_out"], "LOG "
-                 + "Use 'w'/'e'/'s'/'d'/'x'/'c' to move, and 'w' to wait.\n")
-    strong_write(io_db["file_out"], "LOG "
-                 + "Use 'p' to pick up objects, and 'D' to drop them.\n")
-    strong_write(io_db["file_out"], "LOG "
-                 + "Some objects can be used (such as: eaten) by 'u' if "
-                 + "they are in your inventory. "
-                 + "Use 'Up'/'Down' to navigate the inventory.\n")
-    strong_write(io_db["file_out"], "LOG "
-                 + "Use 'l' to toggle 'look' mode (move an exploration cursor "
-                 + "instead of the player over the map).\n")
-    strong_write(io_db["file_out"], "LOG "
-                 + "Use 'PgUp'/PgDn' to scroll the 'Things here' window.\n")
-    strong_write(io_db["file_out"], "LOG See README file for more details.\n")
+    log("Use 'w'/'e'/'s'/'d'/'x'/'c' to move, and 'w' to wait.")
+    log("Use 'p' to pick up objects, and 'D' to drop them.")
+    log("Some objects can be used (such as: eaten) by 'u' if they are in "
+        + "your inventory. Use 'Up'/'Down' to navigate the inventory.")
+    log("Use 'l' to toggle 'look' mode (move an exploration cursor instead of "
+        + "the player over the map).")
+    log("Use 'PgUp'/PgDn' to scroll the 'Things here' window.")
+    log("See README file for more details.")
 
 
 def decrement_lifepoints(t):
@@ -645,9 +695,8 @@ def decrement_lifepoints(t):
         t["T_TYPE"] = world_db["ThingTypes"][t["T_TYPE"]]["TT_CORPSE_ID"]
         if world_db["Things"][0] == t:
             t["fovmap"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2))
-            strong_write(io_db["file_out"], "LOG You die.\n")
-            strong_write(io_db["file_out"],
-                         "LOG See README on how to start over.\n")
+            log("You die.")
+            log("See README on how to start over.")
         else:
             t["fovmap"] = False
             t["T_MEMMAP"] = False
@@ -689,40 +738,33 @@ def mv_yx_in_dir_legal(dir, y, x):
 def enter_altar():  # #
      """What happens when the player enters the altar."""
      if world_db["FAVOR_STAGE"] > 9000:
-        strong_write(io_db["file_out"],
-                     "LOG You step on a soul-less slab of stone.\n")
+        log("You step on a soul-less slab of stone.")
         return
-     strong_write(io_db["file_out"], "LOG YOU ENTER SACRED GROUND.\n")
+     log("YOU ENTER SACRED GROUND.")
      if world_db["FAVOR_STAGE"] == 0:
          world_db["FAVOR_STAGE"] = 1
-         strong_write(io_db["file_out"], "LOG The Island God speaks to you: "
-                      + "\"I don't trust you. You intrude on the island's "
-                      + "affairs. I think you're a nuisance at best, and a "
-                      + "danger to my children at worst. I will give you a "
-                      + "chance to lighten my mood, however: For a while now, "
-                      + "I've been trying to spread the plant "
-                      + world_db["ThingTypes"][world_db["PLANT_0"]]["TT_NAME"]
-                      + " (\""
-                      + world_db["ThingTypes"][world_db["PLANT_0"]]["TT_SYMBOL"]
-                      + "\"). I have not been very successful so far. Maybe "
-                      + "you can make yourself useful there. I will count "
-                      + "each further "
-                      + world_db["ThingTypes"][world_db["PLANT_0"]]["TT_NAME"]
-                      + " that grows to your favor.\n")
+         log("The Island God speaks to you: \"I don't trust you. You intrude "
+              + "on the island's affairs. I think you're a nuisance at best, "
+              + "and a danger to my children at worst. I will give you a "
+              + "chance to lighten my mood, however: For a while now, I've "
+              + "been trying to spread the plant "
+              + world_db["ThingTypes"][world_db["PLANT_0"]]["TT_NAME"] + " (\""
+              + world_db["ThingTypes"][world_db["PLANT_0"]]["TT_SYMBOL"]
+              + "\"). I have not been very successful so far. Maybe you can "
+              + "make yourself useful there. I will count each further "
+              + world_db["ThingTypes"][world_db["PLANT_0"]]["TT_NAME"]
+              + " that grows to your favor.\"")
      elif world_db["FAVOR_STAGE"] == 1 and world_db["GOD_FAVOR"] >= 100:
          world_db["FAVOR_STAGE"] = 2
-         strong_write(io_db["file_out"], "LOG The Island God speaks to you: "
-                      + "\"You could have done worse so far. Maybe you are not "
-                      + "the worst to happen to this island since the metal "
-                      + "birds threw the great lightning ball. Maybe you can "
-                      + "help me spread another plant. It multiplies faster, "
-                      + "and it is highly nutritious: "
-                      + world_db["ThingTypes"][world_db["PLANT_1"]]["TT_NAME"]
-                      + " (\""
-                      + world_db["ThingTypes"][world_db["PLANT_1"]]["TT_SYMBOL"]
-                      + "\"). It is new. I give you the only example. Be very "
-                      + "careful with it! I also give you another tool that "
-                      + "might be helpful.\n")
+         log("The Island God speaks to you: \"You could have done worse so "
+             + "far. Maybe you are not the worst to happen to this island "
+             + "since the metal birds threw the great lightning ball. Maybe "
+             + "you can help me spread another plant. It multiplies faster, "
+             + "and it is highly nutritious: "
+             + world_db["ThingTypes"][world_db["PLANT_1"]]["TT_NAME"] + " (\""
+             + world_db["ThingTypes"][world_db["PLANT_1"]]["TT_SYMBOL"]
+             + "\"). It is new. I give you the only example. Be very careful "
+             + "with it! I also give you another tool that may be helpful.\"")
          id = id_setter(-1, "Things")
          world_db["Things"][id] = new_Thing(world_db["PLANT_1"],
                                             world_db["altar"])
@@ -733,27 +775,26 @@ def enter_altar():  # #
           0 == len([id for id in world_db["Things"]
                    if world_db["Things"][id]["T_TYPE"]
                       == world_db["PLANT_1"]]):
-         strong_write(io_db["file_out"], "LOG The Island God speaks to you: "
-                      + "\"I am greatly disappointed that you lost all "
-                      + world_db["ThingTypes"][world_db["PLANT_1"]]["TT_NAME"]
-                      + " this island had. Here is another one. It cost me "
-                      + " great work. Be more careful this time.\n")
+         log("The Island God speaks to you: \"I am greatly disappointed that "
+             + "you lost all "
+             + world_db["ThingTypes"][world_db["PLANT_1"]]["TT_NAME"]
+             + " this island had. Here is another one. It cost me great work. "
+             + "Be more careful this time.\"")
          id = id_setter(-1, "Things")
          world_db["Things"][id] = new_Thing(world_db["PLANT_1"],
                                             world_db["altar"])
          add_gods_favor(-250)
      elif world_db["GOD_FAVOR"] > 9000:
          world_db["FAVOR_STAGE"] = 9001
-         strong_write(io_db["file_out"], "LOG The Island God speaks to you: "
-                      + "\"You have proven yourself worthy of my respect. "
-                      + "You were a good citizen to the island, and sometimes "
-                      + "a better steward to its inhabitants than me. The "
-                      + "island shall miss you when you leave. But you have "
-                      + "earned the right to do so. Take this "
-                      + world_db["ThingTypes"][world_db["SLIPPERS"]]["TT_NAME"]
-                      + " and USE it when you please. It will take you to "
-                      + "where you came from. (But do feel free to stay here "
-                      + "as long as you like.)\"\n")
+         log("The Island God speaks to you: \"You have proven yourself worthy"
+              + " of my respect. You were a good citizen to the island, and "
+              + "sometimes a better steward to its inhabitants than me. The "
+              + "island shall miss you when you leave. But you have earned "
+              + "the right to do so. Take this "
+              + world_db["ThingTypes"][world_db["SLIPPERS"]]["TT_NAME"]
+              + " and USE it when you please. It will take you to where you "
+              + "came from. (But do feel free to stay here as long as you "
+              + "like.)\"")
          id = id_setter(-1, "Things")
          world_db["Things"][id] = new_Thing(world_db["SLIPPERS"],
                                             world_db["altar"])
@@ -762,7 +803,7 @@ def enter_altar():  # #
 def actor_wait(t):
     """Make t do nothing (but loudly, if player avatar)."""
     if t == world_db["Things"][0]:
-        strong_write(io_db["file_out"], "LOG You wait.\n")
+        log("You wait.")
 
 
 def actor_move(t):
@@ -785,13 +826,11 @@ def actor_move(t):
             if t == world_db["Things"][0]:
                 hitted_type = world_db["Things"][hit_id]["T_TYPE"]
                 hitted_name = world_db["ThingTypes"][hitted_type]["TT_NAME"]
-                strong_write(io_db["file_out"], "LOG You wound " + hitted_name
-                                                + ".\n")
+                log("You wound " + hitted_name + ".")
                 add_gods_favor(-1)  # #
             elif 0 == hit_id:
                 hitter_name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"]
-                strong_write(io_db["file_out"], "LOG " + hitter_name +
-                                                " wounds you.\n")
+                log(hitter_name +" wounds you.")
             test = decrement_lifepoints(world_db["Things"][hit_id])  # #(test=)
             if test and t == world_db["Things"][0]:  # #
                 add_gods_favor(-test)  # #
@@ -807,9 +846,7 @@ def actor_move(t):
             if carries_axe:  # #
                 axe_name = world_db["ThingTypes"][type]["TT_NAME"]  # #
                 if t == world_db["Things"][0]:  # #
-                    strong_write(io_db["file_out"], "LOG With your "  # #
-                                                    + axe_name  # #
-                                                    + ", you chop!\n")  # #
+                    log("With your " + axe_name + ", you chop!\n")  # #
                     if ord("X") == world_db["MAP"][pos]:  # #
                         add_gods_favor(-1)  # #
                 chop_power = world_db["ThingTypes"][type]["TT_TOOLPOWER"]
@@ -820,8 +857,7 @@ def actor_move(t):
                     or (not case_X and  # #
                              0 == int(rand.next() / (3 * chop_power))))):  # #
                     if t == world_db["Things"][0]:  # #
-                        strong_write(io_db["file_out"],  # #
-                                     "LOG You chop it down.\n")  # #
+                        log("You chop it down.")  # #
                     if world_db["MAP"][pos] == ord("X"):  # #
                         add_gods_favor(-10)  # #
                     world_db["MAP"][pos] = ord(".")   # #
@@ -846,18 +882,21 @@ def actor_move(t):
             world_db["Things"][id]["T_POSX"] = move_result[2]
         build_fov_map(t)
         if t == world_db["Things"][0]:
-            strong_write(io_db["file_out"], "LOG You move " + dir + ".\n")
+            log("You move " + dir + ".")
             if (move_result[1] == world_db["altar"][0] and  # #
                 move_result[2] == world_db["altar"][1]):  # #
                 enter_altar()  # #
     elif t == world_db["Things"][0]:
-        strong_write(io_db["file_out"], "LOG You fail to move " + dir + ".\n")
+        log("You fail to move " + dir + ".")
 
 
 def actor_pick_up(t):
-    """Make t pick up (topmost?) Thing from ground into inventory."""
-    # Topmostness is actually not defined so far. Picks most nutritious Thing.
+    """Make t pick up (topmost?) Thing from ground into inventory.
+
+    Define topmostness by how low the thing's type ID is.
+    """
     # 7DRL: Non-player picking up player-dropped consumable -> GOD_FAVOR gain.
+    # 7DRL: Non-players pick up nothing but food of good value to them.
     used_slots = len(t["T_CARRIES"]) # #
     if used_slots < world_db["ThingTypes"][t["T_TYPE"]]["TT_STORAGE"]: # #
         ids = [id for id in world_db["Things"] if world_db["Things"][id] != t
@@ -865,31 +904,33 @@ def actor_pick_up(t):
                if world_db["Things"][id]["T_POSY"] == t["T_POSY"]
                if world_db["Things"][id]["T_POSX"] == t["T_POSX"]]
         if len(ids):
-            highest_id = ids[0]
-            nutritious = 0
-            for id in ids:
-                type = world_db["Things"][id]["T_TYPE"]
-                if world_db["ThingTypes"][type]["TT_TOOL"] == "food" \
-                and world_db["ThingTypes"][type]["TT_TOOLPOWER"] > nutritious:
-                    nutritious = world_db["ThingTypes"][type]["TT_TOOLPOWER"]
-                    highest_id = id
-            world_db["Things"][highest_id]["carried"] = True
-            type = world_db["Things"][highest_id]["T_TYPE"]  # #
+            lowest_tid = -1
+            eat_cost = eat_vs_hunger_threshold(t["T_TYPE"]) # #
+            for iid in ids:
+                tid = world_db["Things"][iid]["T_TYPE"] 
+                if lowest_tid == -1 or tid < lowest_tid:
+                    if (t != world_db["Things"][0] and  # #
+                        (world_db["ThingTypes"][tid]["TT_TOOL"] != "food"  # #
+                         or (world_db["ThingTypes"][tid]["TT_TOOLPOWER"]  # #
+                             <= eat_cost))):  # #
+                        continue  # #
+                    id = iid
+                    lowest_tid = tid
+            world_db["Things"][id]["carried"] = True
+            type = world_db["Things"][id]["T_TYPE"]  # #
             if (t != world_db["Things"][0] # #
-                and world_db["Things"][highest_id]["T_PLAYERDROP"]  # #
+                and world_db["Things"][id]["T_PLAYERDROP"]  # #
                 and world_db["ThingTypes"][type]["TT_TOOL"] == "food"):  # #
                 score = world_db["ThingTypes"][type]["TT_TOOLPOWER"] / 32  # #
                 add_gods_favor(score)  # #
-                world_db["Things"][highest_id]["T_PLAYERDROP"] = 0  # #
-            t["T_CARRIES"].append(highest_id)
+                world_db["Things"][id]["T_PLAYERDROP"] = 0  # #
+            t["T_CARRIES"].append(id)
             if t == world_db["Things"][0]:
-                strong_write(io_db["file_out"], "LOG You pick up an object.\n")
+                log("You pick up an object.")
         elif t == world_db["Things"][0]:
-            err = "You try to pick up an object, but there is none."
-            strong_write(io_db["file_out"], "LOG " + err + "\n")
+            log("You try to pick up an object, but there is none.")
     elif t == world_db["Things"][0]: # #
-        strong_write(io_db["file_out"], "LOG Can't pick up object: " + # #
-                                        "No storage room to carry more.\n") # #
+        log("Can't pick up object: No storage room to carry more.") # #
 
 
 def actor_drop(t):
@@ -900,11 +941,10 @@ def actor_drop(t):
         t["T_CARRIES"].remove(id)
         world_db["Things"][id]["carried"] = False
         if t == world_db["Things"][0]:
-            strong_write(io_db["file_out"], "LOG You drop an object.\n")
+            log("You drop an object.")
             world_db["Things"][id]["T_PLAYERDROP"] = 1  # #
     elif t == world_db["Things"][0]:
-        err = "You try to drop an object, but you own none."
-        strong_write(io_db["file_out"], "LOG " + err + "\n")
+       log("You try to drop an object, but you own none.")
 
 
 def actor_use(t):
@@ -916,33 +956,27 @@ def actor_use(t):
         type = world_db["Things"][id]["T_TYPE"]
         if type == world_db["SLIPPERS"]:  # #
             if t == world_db["Things"][0]:  # #
-                strong_write(io_db["file_out"], "LOG You use the "  # #
-                             + world_db["ThingTypes"][type]["TT_NAME"]  # #
-                             + ". It glows in wondrous colors, and emits "  # #
-                             + "a sound as if from a dying cat. The "  # #
-                             + "Island God laughs.\n")  # #
+                log("You use the " + world_db["ThingTypes"][type]["TT_NAME"]  # #
+                    + ". It glows in wondrous colors, and emits a sound as "  # #
+                    + "if from a dying cat. The Island God laughs.\n")  # #
             t["T_LIFEPOINTS"] = 1  # #
             decrement_lifepoints(t)  # #
         elif (world_db["ThingTypes"][type]["TT_TOOL"] == "axe"  # #
               and t == world_db["Things"][0]):  # #
-                strong_write(io_db["file_out"],  # #
-                             "LOG To use this item for chopping, move "  # #
-                             "towards a tree while carrying it in "  # #
-                             "your inventory.\n")  # #
+                log("To use this item for chopping, move towards a tree while "
+                     + "carrying it in your inventory.")  # #
         elif (world_db["ThingTypes"][type]["TT_TOOL"] == "carpentry"):  # #
             pos = t["T_POSY"] * world_db["MAP_LENGTH"] + t["T_POSX"]
             if (world_db["MAP"][pos] == ord("X")  # #
                 or world_db["MAP"][pos] == ord("|")):  # #
-                strong_write(io_db["file_out"],  # #
-                             "LOG Can't build when standing on barrier.\n")  # #
+                log("Can't build when standing on barrier.")  # #
                 return
             for id in [id for id in world_db["Things"]
                        if not world_db["Things"][id] == t
                        if not world_db["Things"][id]["carried"]
                        if world_db["Things"][id]["T_POSY"] == t["T_POSY"]
                        if world_db["Things"][id]["T_POSX"] == t["T_POSX"]]:
-                 strong_write(io_db["file_out"],
-                              "LOG Can't build when standing objects.\n")  # #
+                 log("Can't build when standing objects.")  # #
                  return
             for id in t["T_CARRIES"]:  # #
                 type_tool = world_db["Things"][id]["T_TYPE"]  # #
@@ -960,29 +994,25 @@ def actor_use(t):
                 t["T_CARRIES"].remove(wood_id)  # #
                 del world_db["Things"][wood_id]  # #
                 world_db["MAP"][pos] = ord("|")  # #
-                strong_write(io_db["file_out"], "LOG With your "  # #
-                             + world_db["ThingTypes"][type_tool]["TT_NAME"]  # #
-                             + " you build a wooden barrier from your "  # #
-                             + world_db["ThingTypes"][type_material]  # #
-                               ["TT_NAME"]  # #
-                             + ".\n")  # #
+                log("With your "  # #
+                    + world_db["ThingTypes"][type_tool]["TT_NAME"]  # #
+                    + " you build a wooden barrier from your "  # #
+                    + world_db["ThingTypes"][type_material]["TT_NAME"]  # #
+                    + ".")  # #
             else:  # #
-                strong_write(io_db["file_out"], "LOG You can't use a "  # #
-                             + world_db["ThingTypes"][type_tool]["TT_NAME"]  # #
-                             + " without some wood in your inventory.\n")  # #
+                log("You can't use a "  # #
+                    + world_db["ThingTypes"][type_tool]["TT_NAME"]  # #
+                    + " without some wood in your inventory.")  # #
         elif world_db["ThingTypes"][type]["TT_TOOL"] == "food":
             t["T_CARRIES"].remove(id)
             del world_db["Things"][id]
             t["T_SATIATION"] += world_db["ThingTypes"][type]["TT_TOOLPOWER"]
             if t == world_db["Things"][0]:
-                strong_write(io_db["file_out"],
-                             "LOG You consume this object.\n")
+                log("You consume this object.")
         elif t == world_db["Things"][0]:
-            strong_write(io_db["file_out"],
-                         "LOG You try to use this object, but fail.\n")
+            log("You try to use this object, but fail.")
     elif t == world_db["Things"][0]:
-        strong_write(io_db["file_out"],
-                     "LOG You try to use an object, but you own none.\n")
+        log("You try to use an object, but you own none.")
 
 
 def thingproliferation(t, prol_map):
@@ -1026,36 +1056,38 @@ def thingproliferation(t, prol_map):
 
 
 def try_healing(t):
-    """Grow t's HP to a 1/32 chance if < HP max, satiation > 0, and waiting.
-
-    On success, decrease satiation score by 32.
-    """
+    """If t's HP < max, increment them if well-nourished, maybe waiting."""
     # 7DRL: Successful heals increment God's mood.
-    if t["T_SATIATION"] > 0 \
-       and t["T_LIFEPOINTS"] < \
-           world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"] \
-       and 0 == (rand.next() % 31) \
-       and t["T_COMMAND"] == [id for id in world_db["ThingActions"]
-                              if world_db["ThingActions"][id]["TA_NAME"] ==
-                                 "wait"][0]:
-        t["T_LIFEPOINTS"] += 1
-        world_db["GOD_MOOD"] += 1  # #
-        t["T_SATIATION"] -= 32
-        if t == world_db["Things"][0]:
-            strong_write(io_db["file_out"], "LOG You heal.\n")
+    if t["T_LIFEPOINTS"] < \
+       world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"]:
+        wait_id = [id for id in world_db["ThingActions"]
+                      if world_db["ThingActions"][id]["TA_NAME"] == "wait"][0]
+        wait_divider = 8 if t["T_COMMAND"] == wait_id else 1
+        testval = int(abs(t["T_SATIATION"]) / wait_divider)
+        if (testval <= 1 or 1 == (rand.next() % testval)):
+            t["T_LIFEPOINTS"] += 1
+            if t != world_db["Things"][0]: # #
+                 world_db["GOD_MOOD"] += 1  # #
+            if t == world_db["Things"][0]:
+                log("You heal.")
+
+
+def hunger_per_turn(type_id):
+    """The amount of satiation score lost per turn for things of given type."""
+    return int(math.sqrt(world_db["ThingTypes"][type_id]["TT_LIFEPOINTS"]))
 
 
 def hunger(t):
     """Decrement t's satiation,dependent on it trigger lifepoint dec chance."""
     if t["T_SATIATION"] > -32768:
-        t["T_SATIATION"] -= 1
-    testbase = t["T_SATIATION"] if t["T_SATIATION"] >= 0 else -t["T_SATIATION"]
-    if not world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"]:
-        raise RuntimeError("A thing that should not hunger is hungering.")
-    stomach = int(32767 / world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"])
-    if int(int(testbase / stomach) / ((rand.next() % stomach) + 1)):
+        #max_hp = world_db["ThingTypes"][t["T_TYPE"]]["TT_LIFEPOINTS"]
+        t["T_SATIATION"] -= hunger_per_turn(t["T_TYPE"]) # int(math.sqrt(max_hp))
+    if 0 != t["T_SATIATION"] and 0 == int(rand.next() / abs(t["T_SATIATION"])):
         if t == world_db["Things"][0]:
-            strong_write(io_db["file_out"], "LOG You suffer from hunger.\n")
+            if t["T_SATIATION"] < 0:
+                log("You suffer from hunger.")
+            else:
+                log("You suffer from over-eating.")
         decrement_lifepoints(t)
 
 
@@ -1115,10 +1147,13 @@ def get_dir_to_target(t, filter):
                                              t["T_LIFEPOINTS"]):
                         return True
         elif t["T_MEMMAP"] and "c" == filter:
+            eat_cost = eat_vs_hunger_threshold(t["T_TYPE"])
             for mt in t["T_MEMTHING"]:
                 if ' ' != chr(t["T_MEMMAP"][(mt[1] * world_db["MAP_LENGTH"])
-                                         + mt[2]]) \
-                   and world_db["ThingTypes"][mt[0]]["TT_TOOL"] == "food":
+                                            + mt[2]]) \
+                   and world_db["ThingTypes"][mt[0]]["TT_TOOL"] == "food" \
+                   and world_db["ThingTypes"][mt[0]]["TT_TOOLPOWER"] \
+                       > eat_cost:
                     return True
         return False
 
@@ -1166,11 +1201,14 @@ def get_dir_to_target(t, filter):
                    world_db["ThingTypes"][Thing["T_TYPE"]]["TT_LIFEPOINTS"]:
                     set_map_score(pos, 0)
         elif "c" == filter:
+            eat_cost = eat_vs_hunger_threshold(t["T_TYPE"])
             for mt in [mt for mt in t["T_MEMTHING"]
                        if ord_blank != t["T_MEMMAP"][mt[1]
                                                     * world_db["MAP_LENGTH"]
                                                     + mt[2]]
-                       if world_db["ThingTypes"][mt[0]]["TT_TOOL"] == "food"]:
+                       if world_db["ThingTypes"][mt[0]]["TT_TOOL"] == "food"
+                       if world_db["ThingTypes"][mt[0]]["TT_TOOLPOWER"]
+                           > eat_cost]:
                 set_map_score(mt[1] * world_db["MAP_LENGTH"] + mt[2], 0)
         elif "s" == filter:
             zero_score_map_where_char_on_memdepthmap(mem_depth_c[0])
@@ -1257,27 +1295,36 @@ def get_dir_to_target(t, filter):
 
 
 def standing_on_food(t):
-    """Return True/False whether t is standing on a consumable."""
+    """Return True/False whether t is standing on healthy consumable."""
+    eat_cost = eat_vs_hunger_threshold(t["T_TYPE"])
     for id in [id for id in world_db["Things"] if world_db["Things"][id] != t
+               if not world_db["Things"][id]["carried"]
                if world_db["Things"][id]["T_POSY"] == t["T_POSY"]
                if world_db["Things"][id]["T_POSX"] == t["T_POSX"]
                if world_db["ThingTypes"][world_db["Things"][id]["T_TYPE"]]
-                  ["TT_TOOL"] == "food"]:
+                  ["TT_TOOL"] == "food"
+               if world_db["ThingTypes"][world_db["Things"][id]["T_TYPE"]]
+                  ["TT_TOOLPOWER"] > eat_cost]:
         return True
     return False
 
 
 def get_inventory_slot_to_consume(t):
-    """Return slot Id of strongest consumable in t's inventory, else -1."""
-    cmp_food = 0
+    """Return invent. slot of healthiest consumable(if any healthy),else -1."""
+    cmp_food = -1
     selection = -1
     i = 0
+    eat_cost = eat_vs_hunger_threshold(t["T_TYPE"])
     for id in t["T_CARRIES"]:
         type = world_db["Things"][id]["T_TYPE"]
         if world_db["ThingTypes"][type]["TT_TOOL"] == "food" \
-           and world_db["ThingTypes"][type]["TT_TOOLPOWER"] > cmp_food:
-            cmp_food = world_db["ThingTypes"][type]["TT_TOOLPOWER"]
-            selection = i
+           and world_db["ThingTypes"][type]["TT_TOOLPOWER"]:
+            nutvalue = world_db["ThingTypes"][type]["TT_TOOLPOWER"]
+            tmp_cmp = abs(t["T_SATIATION"] + nutvalue - eat_cost)
+            if (cmp_food < 0 and tmp_cmp < abs(t["T_SATIATION"])) \
+            or tmp_cmp < cmp_food:
+                cmp_food = tmp_cmp
+                selection = i
         i += 1
     return selection
 
@@ -1436,6 +1483,22 @@ def command_thingshere(str_y, str_x):
             pos = (y * world_db["MAP_LENGTH"]) + x
             strong_write(io_db["file_out"], "THINGS_HERE START\n")
             pos = y * world_db["MAP_LENGTH"] + x;  # #
+            if "v" == chr(world_db["Things"][0]["fovmap"][pos]):
+                for id in [id for tid in sorted(list(world_db["ThingTypes"]))
+                              for id in world_db["Things"]
+                              if not world_db["Things"][id]["carried"]
+                              if world_db["Things"][id]["T_TYPE"] == tid
+                              if y == world_db["Things"][id]["T_POSY"]
+                              if x == world_db["Things"][id]["T_POSX"]]:
+                    type = world_db["Things"][id]["T_TYPE"]
+                    name = world_db["ThingTypes"][type]["TT_NAME"]
+                    strong_write(io_db["file_out"], name + "\n")
+            else:
+                for mt in [mt for tid in sorted(list(world_db["ThingTypes"]))
+                              for mt in world_db["Things"][0]["T_MEMTHING"]
+                              if mt[0] == tid if y == mt[1] if x == mt[2]]:
+                    name = world_db["ThingTypes"][mt[0]]["TT_NAME"]
+                    strong_write(io_db["file_out"], name + "\n")
             if world_db["Things"][0]["T_MEMMAP"][pos] == ord("~"):  # #
                 name = "(terrain: SEA)"  # #
             elif world_db["Things"][0]["T_MEMMAP"][pos] == ord("."):  # #
@@ -1448,20 +1511,9 @@ def command_thingshere(str_y, str_x):
                 name = "(terrain: WALL)"  # #
             elif world_db["Things"][0]["T_MEMMAP"][pos] == ord("_"):  # #
                 name = "(terrain: ALTAR)"  # #
+            else:  # #
+                name = "(?)"  # #
             strong_write(io_db["file_out"], name + "\n")  # #
-            if "v" == chr(world_db["Things"][0]["fovmap"][pos]):
-                for id in world_db["Things"]:
-                    if y == world_db["Things"][id]["T_POSY"] \
-                       and x == world_db["Things"][id]["T_POSX"] \
-                       and not world_db["Things"][id]["carried"]:
-                        type = world_db["Things"][id]["T_TYPE"]
-                        name = world_db["ThingTypes"][type]["TT_NAME"]
-                        strong_write(io_db["file_out"], name + "\n")
-            else:
-                for mt in world_db["Things"][0]["T_MEMTHING"]:
-                    if y == mt[1] and x == mt[2]:
-                        name = world_db["ThingTypes"][mt[0]]["TT_NAME"]
-                        strong_write(io_db["file_out"], name + "\n")
             strong_write(io_db["file_out"], "THINGS_HERE END\n")
         else:
             print("Ignoring: Invalid map coordinates.")
@@ -1957,6 +2009,7 @@ commands_db = {
     "PLANT_1": (1, False, specialtypesetter("PLANT_1")),  # #
     "LUMBER": (1, False, specialtypesetter("LUMBER")),  # #
     "TOOL_0": (1, False, specialtypesetter("TOOL_0")),  # #
+    "EMPATHY": (1, False, setter(None, "EMPATHY", 0, 1)),  # #
     "TA_ID": (1, False, command_taid),
     "TA_EFFORT": (1, False, setter("ThingAction", "TA_EFFORT", 0, 255)),
     "TA_NAME": (1, False, command_taname),
@@ -2011,6 +2064,7 @@ world_db = {
     "PLANT_1": 0,  # #
     "LUMBER": 0,  # #
     "TOOL_0": 0,  # #
+    "EMPATHY": 1,  # #
     "ThingActions": {},
     "ThingTypes": {},
     "Things": {}