content: package rabbitescape.engine.solution;
  
  import rabbitescape.engine.Token;
  import rabbitescape.engine.World;
  import rabbitescape.engine.World.CompletionState;
  import rabbitescape.engine.err.RabbitEscapeException;
  
  public class SolutionExceptions
  {
      /**
       * Base class for everything that can go wrong when running a solution.
       * The member variables get filled in as we roll up the stack through
       * the command we are on, and the solution we are running.
       */
      public static abstract class ProblemRunningSolution
          extends RabbitEscapeException
      {
          private static final long serialVersionUID = 1L;
  
          public int solutionId = -1;
          public int commandIndex = -1;
          public String level = "<>";
          public String world = "";
  
          public ProblemRunningSolution()
          {
          }
  
          public ProblemRunningSolution( Throwable cause )
          {
              super( cause );
          }
      }
  
      /**
       * Indicates that a world was not in the state we asserted it should
       * be in, in our solution.
       */
      public static class UnexpectedState extends ProblemRunningSolution
      {
          private static final long serialVersionUID = 1L;
  
          public final CompletionState expected;
          public final CompletionState actual;
  
          public UnexpectedState(
              CompletionState expected, CompletionState actual )
          {
              this.expected = expected;
              this.actual = actual;
          }
      }
  
      /**
       * Special subclass of UnexpectedState for when the expected state
       * was "WON" - a separate exception so we can give a clearer error message
       * in this common case.
       */
      public static class DidNotWin extends UnexpectedState
      {
          private static final long serialVersionUID = 1L;
  
          public DidNotWin( CompletionState actualState )
          {
              super( CompletionState.WON, actualState );
          }
      }
  
      /**
       * The solution went on longer, but the world was at WON or LOST.
       */
      public static class RanPastEnd extends ProblemRunningSolution
      {
          private static final long serialVersionUID = 1L;
  
          public final CompletionState worldState;
  
          public RanPastEnd( CompletionState worldState )
          {
              this.worldState = worldState;
          }
      }
  
      /**
       * We used a token that we've already used up.
       */
      public static class UsedRunOutAbility extends ProblemRunningSolution
      {
          private static final long serialVersionUID = 1L;
  
          public final Token.Type ability;
  
          public UsedRunOutAbility( Token.Type ability )
          {
              this.ability = ability;
          }
      }
  
      /**
       * We used a token that was not available in this world.
       */
      public static class UsedMissingAbility extends ProblemRunningSolution
      {
          private static final long serialVersionUID = 1L;
  
          public final Token.Type ability;
  
          public UsedMissingAbility( Token.Type ability )
          {
              this.ability = ability;
          }
      }
  
      /**
       * We placed a token outside the size of this world.
       */
      public static class PlacedTokenOutsideWorld extends ProblemRunningSolution
      {
          private static final long serialVersionUID = 1L;
  
          public final int x;
          public final int y;
          public final int worldWidth;
          public final int worldHeight;
  
          public PlacedTokenOutsideWorld(
              int x,
              int y,
              int worldWidth,
              int worldHeight
          )
          {
              this.x = x;
              this.y = y;
              this.worldWidth = worldWidth;
              this.worldHeight = worldHeight;
          }
      }
  
      /**
       * We placed a token but the world wouldn't accept it (presumably because
       * there was a block at that position).
       */
      public static class FailedToPlaceToken extends ProblemRunningSolution
      {
          private static final long serialVersionUID = 1L;
  
          public final int x;
          public final int y;
          public final Token.Type type;
  
          public FailedToPlaceToken(
              int x,
              int y,
              Token.Type type
          )
          {
              this.x = x;
              this.y = y;
              this.type = type;
          }
      }
  
      /**
       * We had an until action, but the level never ended.
       */
      public static class UntilActionNeverEnded extends ProblemRunningSolution
      {
          private static final long serialVersionUID = 1L;
  
          public final World.CompletionState expectedState;
  
          public UntilActionNeverEnded( World.CompletionState expectedState )
          {
              this.expectedState = expectedState;
          }
      }
  
      /**
       * Some other exception was thrown.
       */
      public static class UnknownProblem extends ProblemRunningSolution
      {
          private static final long serialVersionUID = 1L;
  
          public UnknownProblem( Throwable cause )
          {
              super( cause );
          }
      }
  }
  