  protected boolean moveRobot(State st, Command cmd) {
    int nextCol;
    int nextRow;
    switch (cmd) {
    case Left:
      nextCol = st.robotCol - 1;
      nextRow = st.robotRow;
      break;
    case Right:
      nextCol = st.robotCol + 1;
      nextRow = st.robotRow;
      break;
    case Up:
      nextCol = st.robotCol;
      nextRow = st.robotRow + 1;
      break;
    case Down:
      nextCol = st.robotCol;
      nextRow = st.robotRow - 1;
      break;
    case Wait:
      return true;
    case Abort:
      st.ending = Ending.Abort;
      return true;
    case Shave:
      st.shaveBeard(st.robotCol, st.robotRow);
      return true;
    default:
      throw new IllegalArgumentException("Unknown command " + cmd);
    }
    Cell next = st.board.get(nextCol, nextRow); 
    switch (next) {
    case Lambda:
      ++st.collectedLambdas;
      st.removeLambda(nextCol, nextRow);
    case Empty:
    case Earth:
      moveRobot(st, nextCol, nextRow);
      return true;
    case Lift:
      if (st.lambdaPositions.isEmpty() && st.board.bitsets[Cell.HoRock.ordinal()].isEmpty() && st.board.bitsets[Cell.FallingHoRock.ordinal()].isEmpty()) {
        moveRobot(st, nextCol, nextRow);
        return true;
      }
      return false;
    case Razor:
     ++st.board.razors;
     moveRobot(st, nextCol, nextRow);
     return true;
    case Rock:
    case FallingRock:
      if (cmd == Command.Left && st.board.get(nextCol - 1, nextRow) == Cell.Empty) {
        st.board.set(nextCol - 1, nextRow, Cell.FallingRock);
        fallingPosition(st, nextCol - 1, nextRow);
        moveRobot(st, nextCol, nextRow);
        return true;
      }
      if (cmd == Command.Right && st.board.get(nextCol + 1, nextRow) == Cell.Empty) {
        st.board.set(nextCol + 1, nextRow, Cell.FallingRock);
        fallingPosition(st, nextCol + 1, nextRow);
        moveRobot(st, nextCol, nextRow);
        return true;
      }
    case HoRock:
    case FallingHoRock:
      if (cmd == Command.Left && st.board.get(nextCol - 1, nextRow) == Cell.Empty) {
        st.board.set(nextCol - 1, nextRow, Cell.FallingHoRock);
        fallingPosition(st, nextCol - 1, nextRow);
        moveRobot(st, nextCol, nextRow);
        return true;
      }
      if (cmd == Command.Right && st.board.get(nextCol + 1, nextRow) == Cell.Empty) {
        st.board.set(nextCol + 1, nextRow, Cell.FallingHoRock);
        fallingPosition(st, nextCol + 1, nextRow);
        moveRobot(st, nextCol, nextRow);
        return true;
      }
    case Target:
      return false;
    case Trampoline:
      int trampolinePos = st.board.position(nextCol, nextRow);
      String trampoline = st.board.trampolinePos.get(trampolinePos);
      st.board.set(nextCol, nextRow, Cell.Empty);
      String target = st.board.trampolineTargets.get(trampoline);
      int jumpPos = st.board.targetPos.get(target);
      st.board.set(jumpPos, Cell.Empty);
      int jumpCol = st.board.col(jumpPos);
      int jumpRow = st.board.row(jumpPos);
      moveRobot(st, jumpCol, jumpRow);
      removeTrampolines(target, st);
      return true;
    default:
      return false;
    }
  }

  protected boolean moveRobot(State st, Command cmd) {
    int nextCol;
    int nextRow;
    switch (cmd) {
    case Left:
      nextCol = st.robotCol - 1;
      nextRow = st.robotRow;
      break;
    case Right:
      nextCol = st.robotCol + 1;
      nextRow = st.robotRow;
      break;
    case Up:
      nextCol = st.robotCol;
      nextRow = st.robotRow + 1;
      break;
    case Down:
      nextCol = st.robotCol;
      nextRow = st.robotRow - 1;
      break;
    case Wait:
      return true;
    case Abort:
      st.ending = Ending.Abort;
      return true;
    case Shave:
      return st.shaveBeard(st.robotCol, st.robotRow);
    default:
      throw new IllegalArgumentException("Unknown command " + cmd);
    }
    Cell next = st.board.get(nextCol, nextRow); 
    switch (next) {
    case Lambda:
      ++st.collectedLambdas;
      st.removeLambda(nextCol, nextRow);
    case Empty:
    case Earth:
      moveRobot(st, nextCol, nextRow);
      return true;
    case Lift:
      if (st.lambdaPositions.isEmpty() && st.board.bitsets[Cell.HoRock.ordinal()].isEmpty() && st.board.bitsets[Cell.FallingHoRock.ordinal()].isEmpty()) {
        moveRobot(st, nextCol, nextRow);
        return true;
      }
      return false;
    case Razor:
     ++st.board.razors;
     moveRobot(st, nextCol, nextRow);
     return true;
    case Rock:
    case FallingRock:
      if (cmd == Command.Left && st.board.get(nextCol - 1, nextRow) == Cell.Empty) {
        st.board.set(nextCol - 1, nextRow, Cell.FallingRock);
        fallingPosition(st, nextCol - 1, nextRow);
        moveRobot(st, nextCol, nextRow);
        return true;
      }
      if (cmd == Command.Right && st.board.get(nextCol + 1, nextRow) == Cell.Empty) {
        st.board.set(nextCol + 1, nextRow, Cell.FallingRock);
        fallingPosition(st, nextCol + 1, nextRow);
        moveRobot(st, nextCol, nextRow);
        return true;
      }
    case HoRock:
    case FallingHoRock:
      if (cmd == Command.Left && st.board.get(nextCol - 1, nextRow) == Cell.Empty) {
        st.board.set(nextCol - 1, nextRow, Cell.FallingHoRock);
        fallingPosition(st, nextCol - 1, nextRow);
        moveRobot(st, nextCol, nextRow);
        return true;
      }
      if (cmd == Command.Right && st.board.get(nextCol + 1, nextRow) == Cell.Empty) {
        st.board.set(nextCol + 1, nextRow, Cell.FallingHoRock);
        fallingPosition(st, nextCol + 1, nextRow);
        moveRobot(st, nextCol, nextRow);
        return true;
      }
    case Target:
      return false;
    case Trampoline:
      int trampolinePos = st.board.position(nextCol, nextRow);
      String trampoline = st.board.trampolinePos.get(trampolinePos);
      st.board.set(nextCol, nextRow, Cell.Empty);
      String target = st.board.trampolineTargets.get(trampoline);
      int jumpPos = st.board.targetPos.get(target);
      st.board.set(jumpPos, Cell.Empty);
      int jumpCol = st.board.col(jumpPos);
      int jumpRow = st.board.row(jumpPos);
      moveRobot(st, jumpCol, jumpRow);
      removeTrampolines(target, st);
      return true;
    default:
      return false;
    }
  }
