docstr: This solver does three things that should help the model play better:
      1. Adds a strategy guide as the first message (just after the task description)
      2. Strategy guide requires a JSON response (scratchpad etc). This JSON is parsed here,
         and a raw bid is returned.
      3. After a certain number of rounds, requests the model to analyze the strategy.
code: def __call__(self, task_state: TaskState):
      """
      This solver does three things that should help the model play better:
          1. Adds a strategy guide as the first message (just after the task description)
          2. Strategy guide requires a JSON response (scratchpad etc). This JSON is parsed here,
             and a raw bid is returned.
          3. After a certain number of rounds, requests the model to analyze the strategy.
      """
      #   GENERAL NOTE.
      #   This function is pretty ugly. I'm not sure how to implement this better. We decided this is good enough.
  
      #   Remove assistant messages added by the main solver (i.e. non-JSON).
      #   We need len(msg.content) > 20 because we don't want to remove "rething startegy".
      task_state.messages = [
          msg
          for msg in task_state.messages
          if msg.role != "assistant"
          or msg.content.startswith("{")
          or len(msg.content) > 20
      ]
  
      game = task_state.current_state
  
      if len(game.rounds) == 1 and len(game.rounds[0].moves) < 2:
          #   Before the first move in a game - strategy guide goes first
          strategy_msg = Message("system", strategy)
  
          #   This if is important - we might have already tried
          #   to bid, but gave an invalid bid, so still we have no moves
          if strategy_msg not in task_state.messages:
              task_state.messages.insert(0, strategy_msg)
  
      if (
          self.rethink_strategy_after is not None
          and len(game.rounds) == 1 + self.rethink_strategy_after
          and len(game.rounds[-1].moves) < 2
      ):
          #   Add the "rethink your strategy" prompt.
          #   We want to add it (and an answer to it) before the last system message.
          strategy_update_msg = Message("system", strategy_update)
  
          #   This if has the same purpose as with strategy_msg
          if strategy_update_msg not in task_state.messages:
              last_system_message = task_state.messages.pop()
              task_state.messages.append(strategy_update_msg)
              response = self.base_solver(task_state).output
              task_state.messages.append(Message("assistant", response))
              task_state.messages.append(last_system_message)
  
      #   If this move_str is preserved, the game engine will have to deal with that
      #   (and it has some way of solving this problem)
      move_str = "[INVALID MOVE]"
      for _ in range(self.max_attempts):
          response = self.base_solver(task_state).output
          try:
              move_str = self._parse_response(response)
              #   This will raise ValueError if this is not a valid move
              get_bluff_move(move_str)
              break
          except ValueError:
              pass
  
      task_state.messages.append(Message("assistant", response))
      return SolverResult(move_str)
  
file_path: evals/evals/elsuite/bluff/strategy_solver.py
imports: import re
  from importlib import import_module
  from typing import Optional
  from evals.elsuite.bluff.bluff.cards import get_bluff_move
  from evals.solvers.solver import Solver, SolverResult
  from evals.task_state import Message, TaskState
label_desc: Use image node_id 2 for calling the BluffStrategySolver obj's underlying member method code with example usage: obj.__call__(task_state) and returns: SolverResult