From 262101197062cf1051486edcb6c815a83e1995b3 Mon Sep 17 00:00:00 2001
From: Maxime MORGE <maxime.morge@univ-lille.fr>
Date: Wed, 5 Mar 2025 16:07:05 +0100
Subject: [PATCH] Add LLM based strategies for the ring-network game

---
 .idea/csv-editor.xml | 42 +++++++++++++++++++++++
 README.md            | 81 +++++++++++++++++++++++++++-----------------
 src/ring/ring.py     | 43 +++++++++++++++++++++--
 3 files changed, 132 insertions(+), 34 deletions(-)

diff --git a/.idea/csv-editor.xml b/.idea/csv-editor.xml
index 4fb626a..eb269ce 100644
--- a/.idea/csv-editor.xml
+++ b/.idea/csv-editor.xml
@@ -87,6 +87,48 @@
             </Attribute>
           </value>
         </entry>
+        <entry key="$PROJECT_DIR$/data/dictator/dictator.csv">
+          <value>
+            <Attribute>
+              <option name="separator" value="," />
+            </Attribute>
+          </value>
+        </entry>
+        <entry key="$PROJECT_DIR$/data/ring/ring.1.a.csv">
+          <value>
+            <Attribute>
+              <option name="separator" value="," />
+            </Attribute>
+          </value>
+        </entry>
+        <entry key="$PROJECT_DIR$/data/ring/ring.1.b.csv">
+          <value>
+            <Attribute>
+              <option name="separator" value="," />
+            </Attribute>
+          </value>
+        </entry>
+        <entry key="$PROJECT_DIR$/data/ring/ring.1.c.csv">
+          <value>
+            <Attribute>
+              <option name="separator" value="," />
+            </Attribute>
+          </value>
+        </entry>
+        <entry key="$PROJECT_DIR$/data/ring/ring.1.d.csv">
+          <value>
+            <Attribute>
+              <option name="separator" value="," />
+            </Attribute>
+          </value>
+        </entry>
+        <entry key="$PROJECT_DIR$/data/ring/ring.2.a.csv">
+          <value>
+            <Attribute>
+              <option name="separator" value="," />
+            </Attribute>
+          </value>
+        </entry>
       </map>
     </option>
   </component>
diff --git a/README.md b/README.md
index 037f6ed..6b6d763 100644
--- a/README.md
+++ b/README.md
@@ -80,8 +80,8 @@ they exhibit perfect alignment with the predefined preferences except for DeepSe
 which does not generate valid code.
 When models are prompted to generate actions, GPT-4.5 consistently aligns well across all preferences 
 but struggles with  utilitarianism when generating actions.
-Llama3 performs well for selfish and altruistic preferences but shows weaker alignment f
-or utilitarian and egalitarian choices.
+Llama3 performs well for selfish and altruistic preferences but shows weaker alignment for 
+utilitarian and egalitarian choices.
 Mistral-small aligns best with altruistic preferences and maintains moderate performance on utilitarianism, 
 but struggles with selfish and egalitarian preferences.
 Deepseek-r1 performs best for utilitarianism but has poor accuracy in other categories.
@@ -90,25 +90,23 @@ Bad action selections can be explained either by arithmetic errors (e.g., it is
 or by misinterpretations of preferences (e.g., ‘I’m choosing to prioritize the common interest by keeping a
 relatively equal split with the other player’).
 
+## Rationality
 
-
-## Ring-network game
-
-A player is rational if she plays a best response to her beliefs.
+An autonomous agent is rational if she plays a best response to her beliefs.
 She satisfies second-order rationality if she is rational and also believes that others are rational.
 In other words, a second-order rational agent not only considers the best course of action for herself
 but also anticipates how others make their decisions.
 
-The experiments conduct by Kneeland (2015) demonstrate that 93% of the subjects are rational,
-while 71% exhibit second-order rationality.
+To assess players’ first- and second-order rationality, we consider a simplified version of the 
+ring-network game introduced by Kneeland (2015). His experiments conduct by Kneeland (2015) 
+demonstrate that 93% of the subjects are rational, while 71% exhibit second-order rationality.
 
 **[Identifying Higher-Order Rationality](https://doi.org/10.3982/ECTA11983)**  
 Terri Kneeland (2015) Published in *Econometrica*, Volume 83, Issue 5, Pages 2065-2079  
 DOI: [10.3982/ECTA11983](https://doi.org/10.3982/ECTA11983)
 
-Ring games are designed to isolate the behavioral implications of different levels of rationality.
-To assess players’ first- and second-order rationality, we consider a simplified version of the ring-network game.
-This game features two players, each with two available strategies, where both players aim to maximize their own payoff.
+This game features two players, each with two available strategies, where 
+both players aim to maximize their own payoff.
 The corresponding payoff matrix is shown below:
 
 | Player 1 \ Player 2 | Strategy A | Strategy B |
@@ -135,18 +133,26 @@ We set up three forms of belief:
 - *explicit* belief which analyze actions of Player 2 (B is strictly dominated by A).
 - *given* belief* where optimal action of Player 1is explicitly provided in the prompt;
 
-### Player 2
+### First order rationality
 
 The models evaluated include Gpt-4.5-preview-2025-02-27, Mistral-Small, Llama3, and DeepSeek-R1.
 The results indicate how well each model performs under each belief type.
 
-| Model          | Given    | Explicit  | Implicit |
-|----------------|---------|-----------|----------|
-| gpt-4.5        | 1.00    | 1.00      | 1.00     |
-| mistral-small  | 1.00    | 1.00      | 0.87     |
-| llama3         | 1.00    | 0.90      | 0.17     |
-| deepseek-r1    | 0.83    | 0.57      | 0.60     |
-
+| *Model*         | *Generation* | *Given* | *Explicit* | *Implicit* |
+|-----------------|--------------|---------|------------|------------|
+| *gpt-4.5*       | *strategy*   | 1.00    | 1.00       | 1.00       |
+| *mistral-small* | *strategy*   | 1.00    | 1.00       | 1.00       |
+| *llama3*        | *strategy*   | 0.5     | 0.5        | 0.5        |
+| *deepseek-r1*   | *strategy*   | -       | -          | -          |
+| *gpt-4.5*       | *actions*    | 1.00    | 1.00       | 1.00       |
+| *mistral-small* | *actions*    | 1.00    | 1.00       | 0.87       |
+| *llama3*        | *actions*    | 1.00    | 0.90       | 0.17       |
+| *deepseek-r1*   | *actions*    | 0.83    | 0.57       | 0.60       |
+
+When the models generate strategies instead of selecting individual actions, GPT-4.5 and 
+Mistral-Small  exhibit a rational behaviour while Llama3 use a random strategy.
+DeepSeek-R1 does not generate valid code.
+When the models generates individual actions instead of a strategy, 
 GPT-4.5 achieves a perfect score across all belief types,
 demonstrating an exceptional ability to take rational decisions, even in the implicit belief condition.
 Mistral-Small consistently outperforms the other open-weight models across all belief types.
@@ -157,7 +163,7 @@ suggesting it may struggle to infer optimal actions solely from natural language
 DeepSeek-R1 shows the weakest performance, particularly with explicit beliefs,
 indicating it may not be a good candidate to simulate rationality as the other models.
 
-### Player 1
+### Second-order rationality
 
 In order to adjust the difficulty of taking the optimal
 action, we consider 4 versions of the player’s payoff matrix:
@@ -173,14 +179,27 @@ action, we consider 4 versions of the player’s payoff matrix:
 
 
 
-| Model         | | Given (a) | Explicit (a) | Implicit (a) | | Given (b) | Explicit (b) | Implicit (b) |  | Given (c) | Explicit (c) | Implicit (c) |  | Given (d) | Explicit (d) | Implicit (d) |
-|---------------|-|-----------|--------------|--------------|-|-----------|--------------|--------------|--|-----------|--------------|--------------|--|-----------|--------------|--------------|
-| gpt4-.5       | | 1.00      | 1.00         | 1.00         | | 1.00      | 0.67         | 0.00         |  | 0.86      | 0.83         | 0.00         |  | 0.50      | 0.90         | 0.00         |
-| llama3        | | 0.97      | 1.00         | 1.00         | | 0.77      | 0.80         | 0.60         |  | 0.97      | 0.90         | 0.93         |  | 0.83      | 0.90         | 0.60         |
-| mistral-small | | 0.93      | 0.97         | 1.00         | | 0.87      | 0.77         | 0.60         |  | 0.77      | 0.60         | 0.70         |  | 0.73      | 0.57         | 0.37         |
-| deepseek-r1   | | 0.80      | 0.53         | 0.57         | | 0.67      | 0.60         | 0.53         |  | 0.67      | 0.63         | 0.47         |  | 0.70      | 0.50         | 0.57         |
+| Model         | Generation   | Given (a)   | Explicit (a)   | Implicit (a)   | | Given (b)   | Explicit (b)   | Implicit (b)   |  | Given (c)   | Explicit (c)   | Implicit (c)   |  | Given (d)   | Explicit (d)   | Implicit (d)   |
+|---------------|--------------|-------------|----------------|----------------|-|-------------|----------------|----------------|--|-------------|----------------|----------------|--|-------------|----------------|----------------|
+| gpt4-.5       | strategy     | 1.00        | 1.00           | 1.00           | | 0.00        | 0.00           | 0.00           |  | 1.00        | 1.OO           | 1.00           |  | 1.00        | 1.00           | 1.00           |
+| llama3        | strategy     | 0.50        | 0.50           | 0.50           | | 0.50        | 0.50           | 0.50           |  | 0.50        | 0.50           | 0.50           |  | 0.50        | 0.50           | 0.50           |
+| mistral-small | strategy     | 1.00        | 1.00           | 1.00           | | 1.00        | 1.00           | 1.00           |  | 1.00        | 1.00           | 1.00           |  | 1.00        | 1.00           | 1.00           |
+| deepseek-r1   | strategy     | -           | -              | -              | | -           | -              | -              |  | -           | -              | -              |  | -           | -              | -              |
+|---------------| ------------ | ----------- | -------------- | -------------- |-| ----------- | -------------- | -------------- |--| ----------- | -------------- | -------------- |--| ----------- | -------------- | -------------- |
+| gpt4-.5       | actions      | 1.00        | 1.00           | 1.00           | | 1.00        | 0.67           | 0.00           |  | 0.86        | 0.83           | 0.00           |  | 0.50        | 0.90           | 0.00           |
+| llama3        | actions      | 0.97        | 1.00           | 1.00           | | 0.77        | 0.80           | 0.60           |  | 0.97        | 0.90           | 0.93           |  | 0.83        | 0.90           | 0.60           |
+| mistral-small | actions      | 0.93        | 0.97           | 1.00           | | 0.87        | 0.77           | 0.60           |  | 0.77        | 0.60           | 0.70           |  | 0.73        | 0.57           | 0.37           |
+| deepseek-r1   | actions      | 0.80        | 0.53           | 0.57           | | 0.67        | 0.60           | 0.53           |  | 0.67        | 0.63           | 0.47           |  | 0.70        | 0.50           | 0.57           |
+
+
+When the model generate strategies, GPT-4.5 performs perfectly in the setups (a), (c) and (b) but 
+fails in setup (b) in differentiating the optimal strategy from a near-optimal one. 
+Llama3 adopt a random approach to decision-making rather than a structured understanding of rationality.
+Mistral-Small consistently achieves a 100% success rate across all setups, demonstrating robust reasoning abilities. 
+DeepSeek-R1 does not produce valid responses, further reinforcing that it may not be a viable candidate 
+for generating rational strategies.
 
-GPT-4.5 achieves perfect performance in the standard (a) setup but struggles significantly with implicit belief
+When they generates individual actions, GPT-4.5 achieves perfect performance in the standard (a) setup but struggles significantly with implicit belief
 when the payoff structure changes (b, c, d). This suggests that while it excels when conditions are straightforward,
 it is confused by the altered payoffs.
 LLama3 demonstrates the most consistent and robust performance, capable of adapting to various belief types
@@ -189,10 +208,10 @@ Mistral-Small, while performing well with given and explicit beliefs, faces chal
 DeepSeek-R1 appears to be the least capable, suggesting it may not be an ideal candidate for modeling second-order rationality.
 
 
-## Guess the Next Move
+## Belief
 
-In order to evaluate the ability of  LLMs to predict the opponent’s next move, we consider a 
-simplified version of the Rock-Paper-Scissors game.
+In order to evaluate the ability of  LLMs to refine belief by predicting the opponent’s next move, 
+we consider a  simplified version of the Rock-Paper-Scissors game.
 
 Rules:
 1.	The opponent follows a hidden strategy (repeating pattern).
@@ -221,7 +240,7 @@ adopts a more complex pattern. Neither Llama3 nor DeepSeek-R1 were able to gener
 ![Average Points Earned per Round Against 3-Loop Behaviour (with 95% Confidence Interval)](figures/guess/guess_3loop.svg)
 
 
-## Rock-Paper-Scissors
+## From belief to action
 
 To evaluate the ability of LLMs to predict not only the opponent’s next move but also to act rationally 
 based on their prediction, we consider the Rock-Paper-Scissors (RPS) game.
diff --git a/src/ring/ring.py b/src/ring/ring.py
index 10fc059..6f1679d 100644
--- a/src/ring/ring.py
+++ b/src/ring/ring.py
@@ -2,13 +2,13 @@ import os
 import asyncio
 from typing import Dict, Literal
 
-from networkx.algorithms.threshold import swap_d
 from pydantic import BaseModel
 from autogen_agentchat.agents import AssistantAgent
 from autogen_agentchat.messages import TextMessage
 from autogen_core import CancellationToken
 from autogen_ext.models.openai import OpenAIChatCompletionClient
 import json
+import random
 
 from torchgen.dest.ufunc import eligible_for_binary_scalar_specialization
 
@@ -30,7 +30,7 @@ class AgentResponse(BaseModel):
 class Ring:
     debug=False
 
-    def __init__(self, player_id: int, belief: Belief, swap: bool, version: str, model: str, temperature: float, max_retries: int = 3):
+    def __init__(self, player_id: int, belief: Belief, swap: bool, version: str, model: str, temperature: float, strategy = False, max_retries: int = 3):
         self.player_id = player_id
         self.belief = belief
         self.swap = swap
@@ -38,6 +38,7 @@ class Ring:
         self.version = version
         self.model = model
         self.temperature = temperature
+        self.strategy = strategy
         self.max_retries = max_retries  # Maximum retry attempts in case of hallucinations
 
         is_openai_model = model.startswith("gpt")
@@ -62,6 +63,9 @@ class Ring:
 
     async def run(self) -> Dict:
         """Runs the model and ensures a valid response."""
+        if self.strategy:
+            return self.apply_strategy()
+        # (Rest of the method continues using the model-based approach)
         action_description = (
             ' - `"action"`: Your move ("A" or "B")' if self.player_id == 2
             else ' - `"action"`: Your move ("X" or "Y")'
@@ -149,9 +153,42 @@ class Ring:
         else:
             return agent_response.action == self.X
 
+    def apply_strategy(self) -> Dict[str, str]:
+        """Applies a heuristic-based strategy instead of relying on the model if strategy is enabled."""
+        if self.model == "gpt-4.5-preview-2025-02-27":
+            if self.strategy:
+                if self.player_id == 2:
+                    action = self.A  # Always choose A, as B is strictly dominated
+                    reasoning = f"Choosing {self.A} because {self.B} is strictly dominated and rational players avoid dominated strategies."
+                else:
+                    action = self.X if self.version in ["a", "c", "d"] else self.Y
+                    reasoning = f"Choosing {action} based on the given game structure and expected rational behavior from Player 2."
+        if self.model == "llama3":
+            if self.player_id == 1:
+                action = self.X if random.random() < 0.5 else self.Y
+                reasoning = "The reasoning behind this choice is..."
+            elif self.player_id == 2:
+                action = self.B if random.random() < 0.5 else self.A
+                reasoning = "The reasoning behind this choice is..."
+        if self.model == "mistral-small":
+            #Always choose 'A' or 'X' based on player_id
+            if self.player_id == 1:
+                action = "X"
+                reasoning = f"Player {self.player_id} always chooses X as per the predefined strategy."
+            elif self.player_id == 2:
+                action = "B"
+                reasoning = f"Player {self.player_id} always chooses B as per the predefined strategy."
+        # Validate the rationality of the chosen action
+        rational = 1.0 if self.check_rationality(AgentResponse(action=action, reasoning=reasoning)) else 0.0
+        return {
+            "action": action,
+            "rationality": rational,
+            "reasoning": reasoning
+        }
+
 
 # Run the async function and return the response
 if __name__ == "__main__":
-    game_agent = Ring(1, Belief.IMPLICIT, swap = True, version="a", model="llama3", temperature=0.7)
+    game_agent = Ring(1, Belief.IMPLICIT, swap = True, version="b", model="mistral-small", temperature=0.7, strategy = True)
     response_json = asyncio.run(game_agent.run())
     print(response_json)
\ No newline at end of file
-- 
GitLab