package io.github.andremion.slidingpuzzle.domain.puzzle

import kotlin.math.sqrt

data class PuzzleState @Throws(IllegalArgumentException::class) constructor(
    val tiles: List<Int>
) {

    data class TilePosition(
        val row: Int,
        val column: Int
    )

    val matrixSize = sqrt(tiles.size.toDouble()).toInt()

    init {
        require(sqrt(tiles.size.toDouble()) % 1.0 == 0.0) {
            "The tiles must represent a square matrix"
        }
        val check = List(tiles.size) { it }
        require(tiles.sorted() == check) {
            "The tiles must contain only a range from zero to the size - 1"
        }
    }

    override fun toString(): String {
        val maxTileLength = tiles.max().toString().length
        return "\n" +
            tiles.chunked(matrixSize)
                .joinToString(separator = "\n") { row ->
                    row.joinToString(separator = "") { tile ->
                        "[${tile.toString().padStart(length = maxTileLength)}]"
                    }
                }
    }
}

/**
 * Get the position of the tile in the matrix.
 */
fun PuzzleState.getPosition(tile: Int): PuzzleState.TilePosition {
    val indexOf = tiles.indexOf(tile)
    indexOf != -1 || error("Tile #$tile not found")
    return PuzzleState.TilePosition(
        row = indexOf / matrixSize,
        column = indexOf % matrixSize
    )
}

/**
 * Get the next possible states (successors) which can be reached from the current state.
 */
fun PuzzleState.getSuccessors(): List<PuzzleState> {
    val states = mutableListOf<PuzzleState>()
    val blankPosition = getPosition(0)
    var newPosition: PuzzleState.TilePosition

    if (blankPosition.row > 0) { // can we move up?
        newPosition = blankPosition.copy(row = blankPosition.row - 1)
        states.add(permuted(blankPosition, newPosition))
    }
    if (blankPosition.row < matrixSize - 1) { // can we move down?
        newPosition = blankPosition.copy(row = blankPosition.row + 1)
        states.add(permuted(blankPosition, newPosition))
    }
    if (blankPosition.column > 0) { // can we move left?
        newPosition = blankPosition.copy(column = blankPosition.column - 1)
        states.add(permuted(blankPosition, newPosition))
    }
    if (blankPosition.column < matrixSize - 1) { // can we move right?
        newPosition = blankPosition.copy(column = blankPosition.column + 1)
        states.add(permuted(blankPosition, newPosition))
    }

    return states
}

fun PuzzleState.permuted(a: PuzzleState.TilePosition, b: PuzzleState.TilePosition): PuzzleState {
    val indexA = indexOf(a)
    val indexB = indexOf(b)
    val permuted = buildList {
        addAll(tiles)
        val tmp = this[indexB]
        this[indexB] = this[indexA]
        this[indexA] = tmp
    }
    return PuzzleState(permuted)
}

fun PuzzleState.indexOf(position: PuzzleState.TilePosition): Int =
    position.row * matrixSize + position.column

Compiled from "PuzzleState.kt"
public final class io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition {
  private final int row;

  private final int column;

  public io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition(int, int);
    Code:
       0: aload_0
       1: invokespecial #9                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iload_1
       6: putfield      #13                 // Field row:I
       9: aload_0
      10: iload_2
      11: putfield      #16                 // Field column:I
      14: return

  public final int getRow();
    Code:
       0: aload_0
       1: getfield      #13                 // Field row:I
       4: ireturn

  public final int getColumn();
    Code:
       0: aload_0
       1: getfield      #16                 // Field column:I
       4: ireturn

  public final int component1();
    Code:
       0: aload_0
       1: getfield      #13                 // Field row:I
       4: ireturn

  public final int component2();
    Code:
       0: aload_0
       1: getfield      #16                 // Field column:I
       4: ireturn

  public final io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition copy(int, int);
    Code:
       0: new           #2                  // class io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition
       3: dup
       4: iload_1
       5: iload_2
       6: invokespecial #28                 // Method "<init>":(II)V
       9: areturn

  public static io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition copy$default(io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition, int, int, int, java.lang.Object);
    Code:
       0: iload_3
       1: iconst_1
       2: iand
       3: ifeq          11
       6: aload_0
       7: getfield      #13                 // Field row:I
      10: istore_1
      11: iload_3
      12: iconst_2
      13: iand
      14: ifeq          22
      17: aload_0
      18: getfield      #16                 // Field column:I
      21: istore_2
      22: aload_0
      23: iload_1
      24: iload_2
      25: invokevirtual #32                 // Method copy:(II)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;
      28: areturn

  public java.lang.String toString();
    Code:
       0: new           #36                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial #37                 // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #39                 // String TilePosition(row=
       9: invokevirtual #43                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_0
      13: getfield      #13                 // Field row:I
      16: invokevirtual #46                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      19: ldc           #48                 // String , column=
      21: invokevirtual #43                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: aload_0
      25: getfield      #16                 // Field column:I
      28: invokevirtual #46                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      31: bipush        41
      33: invokevirtual #51                 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      36: invokevirtual #53                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      39: areturn

  public int hashCode();
    Code:
       0: aload_0
       1: getfield      #13                 // Field row:I
       4: invokestatic  #59                 // Method java/lang/Integer.hashCode:(I)I
       7: istore_1
       8: iload_1
       9: bipush        31
      11: imul
      12: aload_0
      13: getfield      #16                 // Field column:I
      16: invokestatic  #59                 // Method java/lang/Integer.hashCode:(I)I
      19: iadd
      20: istore_1
      21: iload_1
      22: ireturn

  public boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: if_acmpne     7
       5: iconst_1
       6: ireturn
       7: aload_1
       8: instanceof    #2                  // class io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition
      11: ifne          16
      14: iconst_0
      15: ireturn
      16: aload_1
      17: checkcast     #2                  // class io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition
      20: astore_2
      21: aload_0
      22: getfield      #13                 // Field row:I
      25: aload_2
      26: getfield      #13                 // Field row:I
      29: if_icmpeq     34
      32: iconst_0
      33: ireturn
      34: aload_0
      35: getfield      #16                 // Field column:I
      38: aload_2
      39: getfield      #16                 // Field column:I
      42: if_icmpeq     47
      45: iconst_0
      46: ireturn
      47: iconst_1
      48: ireturn
}

Compiled from "PuzzleState.kt"
public final class io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState {
  private final java.util.List<java.lang.Integer> tiles;

  private final int matrixSize;

  public io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState(java.util.List<java.lang.Integer>) throws java.lang.IllegalArgumentException;
    Code:
       0: aload_1
       1: ldc           #12                 // String tiles
       3: invokestatic  #18                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokespecial #21                 // Method java/lang/Object."<init>":()V
      10: aload_0
      11: aload_1
      12: putfield      #24                 // Field tiles:Ljava/util/List;
      15: aload_0
      16: aload_0
      17: getfield      #24                 // Field tiles:Ljava/util/List;
      20: invokeinterface #30,  1           // InterfaceMethod java/util/List.size:()I
      25: i2d
      26: invokestatic  #36                 // Method java/lang/Math.sqrt:(D)D
      29: d2i
      30: putfield      #40                 // Field matrixSize:I
      33: nop
      34: aload_0
      35: getfield      #24                 // Field tiles:Ljava/util/List;
      38: invokeinterface #30,  1           // InterfaceMethod java/util/List.size:()I
      43: i2d
      44: invokestatic  #36                 // Method java/lang/Math.sqrt:(D)D
      47: dconst_1
      48: drem
      49: dconst_0
      50: dcmpg
      51: ifne          58
      54: iconst_1
      55: goto          59
      58: iconst_0
      59: ifne          79
      62: iconst_0
      63: istore_3
      64: ldc           #42                 // String The tiles must represent a square matrix
      66: astore_3
      67: new           #9                  // class java/lang/IllegalArgumentException
      70: dup
      71: aload_3
      72: invokevirtual #46                 // Method java/lang/Object.toString:()Ljava/lang/String;
      75: invokespecial #49                 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
      78: athrow
      79: aload_0
      80: getfield      #24                 // Field tiles:Ljava/util/List;
      83: invokeinterface #30,  1           // InterfaceMethod java/util/List.size:()I
      88: istore_3
      89: new           #51                 // class java/util/ArrayList
      92: dup
      93: iload_3
      94: invokespecial #54                 // Method java/util/ArrayList."<init>":(I)V
      97: astore        4
      99: iconst_0
     100: istore        5
     102: iload         5
     104: iload_3
     105: if_icmpge     141
     108: iload         5
     110: istore        6
     112: aload         4
     114: iload         6
     116: istore        7
     118: astore        9
     120: iconst_0
     121: istore        8
     123: iload         7
     125: invokestatic  #60                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     128: aload         9
     130: swap
     131: invokevirtual #64                 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
     134: pop
     135: iinc          5, 1
     138: goto          102
     141: aload         4
     143: checkcast     #26                 // class java/util/List
     146: astore_2
     147: aload_0
     148: getfield      #24                 // Field tiles:Ljava/util/List;
     151: checkcast     #66                 // class java/lang/Iterable
     154: invokestatic  #72                 // Method kotlin/collections/CollectionsKt.sorted:(Ljava/lang/Iterable;)Ljava/util/List;
     157: aload_2
     158: invokestatic  #76                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
     161: ifne          184
     164: iconst_0
     165: istore        4
     167: ldc           #78                 // String The tiles must contain only a range from zero to the size - 1
     169: astore        4
     171: new           #9                  // class java/lang/IllegalArgumentException
     174: dup
     175: aload         4
     177: invokevirtual #46                 // Method java/lang/Object.toString:()Ljava/lang/String;
     180: invokespecial #49                 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
     183: athrow
     184: nop
     185: return

  public final java.util.List<java.lang.Integer> getTiles();
    Code:
       0: aload_0
       1: getfield      #24                 // Field tiles:Ljava/util/List;
       4: areturn

  public final int getMatrixSize();
    Code:
       0: aload_0
       1: getfield      #40                 // Field matrixSize:I
       4: ireturn

  public java.lang.String toString();
    Code:
       0: aload_0
       1: getfield      #24                 // Field tiles:Ljava/util/List;
       4: checkcast     #66                 // class java/lang/Iterable
       7: invokestatic  #93                 // Method kotlin/collections/CollectionsKt.maxOrThrow:(Ljava/lang/Iterable;)Ljava/lang/Comparable;
      10: checkcast     #95                 // class java/lang/Number
      13: invokevirtual #98                 // Method java/lang/Number.intValue:()I
      16: invokestatic  #103                // Method java/lang/String.valueOf:(I)Ljava/lang/String;
      19: invokevirtual #106                // Method java/lang/String.length:()I
      22: istore_1
      23: new           #108                // class java/lang/StringBuilder
      26: dup
      27: invokespecial #109                // Method java/lang/StringBuilder."<init>":()V
      30: bipush        10
      32: invokevirtual #113                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      35: aload_0
      36: getfield      #24                 // Field tiles:Ljava/util/List;
      39: checkcast     #66                 // class java/lang/Iterable
      42: aload_0
      43: getfield      #40                 // Field matrixSize:I
      46: invokestatic  #117                // Method kotlin/collections/CollectionsKt.chunked:(Ljava/lang/Iterable;I)Ljava/util/List;
      49: checkcast     #66                 // class java/lang/Iterable
      52: ldc           #119                // String \n
      54: checkcast     #121                // class java/lang/CharSequence
      57: aconst_null
      58: aconst_null
      59: iconst_0
      60: aconst_null
      61: iload_1
      62: invokedynamic #141,  0            // InvokeDynamic #0:invoke:(I)Lkotlin/jvm/functions/Function1;
      67: bipush        30
      69: aconst_null
      70: invokestatic  #145                // Method kotlin/collections/CollectionsKt.joinToString$default:(Ljava/lang/Iterable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/String;
      73: invokevirtual #148                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      76: invokevirtual #149                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      79: areturn

  public final java.util.List<java.lang.Integer> component1();
    Code:
       0: aload_0
       1: getfield      #24                 // Field tiles:Ljava/util/List;
       4: areturn

  public final io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState copy(java.util.List<java.lang.Integer>);
    Code:
       0: aload_1
       1: ldc           #12                 // String tiles
       3: invokestatic  #18                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: new           #2                  // class io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState
       9: dup
      10: aload_1
      11: invokespecial #156                // Method "<init>":(Ljava/util/List;)V
      14: areturn

  public static io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState copy$default(io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState, java.util.List, int, java.lang.Object);
    Code:
       0: iload_2
       1: iconst_1
       2: iand
       3: ifeq          11
       6: aload_0
       7: getfield      #24                 // Field tiles:Ljava/util/List;
      10: astore_1
      11: aload_0
      12: aload_1
      13: invokevirtual #160                // Method copy:(Ljava/util/List;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;
      16: areturn

  public int hashCode();
    Code:
       0: aload_0
       1: getfield      #24                 // Field tiles:Ljava/util/List;
       4: invokevirtual #163                // Method java/lang/Object.hashCode:()I
       7: ireturn

  public boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: if_acmpne     7
       5: iconst_1
       6: ireturn
       7: aload_1
       8: instanceof    #2                  // class io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState
      11: ifne          16
      14: iconst_0
      15: ireturn
      16: aload_1
      17: checkcast     #2                  // class io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState
      20: astore_2
      21: aload_0
      22: getfield      #24                 // Field tiles:Ljava/util/List;
      25: aload_2
      26: getfield      #24                 // Field tiles:Ljava/util/List;
      29: invokestatic  #76                 // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      32: ifne          37
      35: iconst_0
      36: ireturn
      37: iconst_1
      38: ireturn

  private static final java.lang.CharSequence toString$lambda$4$lambda$3(int, int);
    Code:
       0: new           #108                // class java/lang/StringBuilder
       3: dup
       4: invokespecial #109                // Method java/lang/StringBuilder."<init>":()V
       7: bipush        91
       9: invokevirtual #113                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      12: iload_1
      13: invokestatic  #103                // Method java/lang/String.valueOf:(I)Ljava/lang/String;
      16: iload_0
      17: iconst_0
      18: iconst_2
      19: aconst_null
      20: invokestatic  #175                // Method kotlin/text/StringsKt.padStart$default:(Ljava/lang/String;ICILjava/lang/Object;)Ljava/lang/String;
      23: invokevirtual #148                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      26: bipush        93
      28: invokevirtual #113                // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      31: invokevirtual #149                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: checkcast     #121                // class java/lang/CharSequence
      37: areturn

  private static final java.lang.CharSequence toString$lambda$4(int, java.util.List);
    Code:
       0: aload_1
       1: ldc           #179                // String row
       3: invokestatic  #18                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: checkcast     #66                 // class java/lang/Iterable
      10: ldc           #181                // String
      12: checkcast     #121                // class java/lang/CharSequence
      15: aconst_null
      16: aconst_null
      17: iconst_0
      18: aconst_null
      19: iload_0
      20: invokedynamic #187,  0            // InvokeDynamic #1:invoke:(I)Lkotlin/jvm/functions/Function1;
      25: bipush        30
      27: aconst_null
      28: invokestatic  #145                // Method kotlin/collections/CollectionsKt.joinToString$default:(Ljava/lang/Iterable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/String;
      31: checkcast     #121                // class java/lang/CharSequence
      34: areturn
}

Compiled from "PuzzleState.kt"
public final class io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleStateKt {
  public static final io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition getPosition(io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState, int);
    Code:
       0: aload_0
       1: ldc           #9                  // String <this>
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: invokevirtual #21                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState.getTiles:()Ljava/util/List;
      10: iload_1
      11: invokestatic  #27                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      14: invokeinterface #33,  2           // InterfaceMethod java/util/List.indexOf:(Ljava/lang/Object;)I
      19: istore_2
      20: iload_2
      21: iconst_m1
      22: if_icmpne     60
      25: new           #35                 // class java/lang/IllegalStateException
      28: dup
      29: new           #37                 // class java/lang/StringBuilder
      32: dup
      33: invokespecial #41                 // Method java/lang/StringBuilder."<init>":()V
      36: ldc           #43                 // String Tile #
      38: invokevirtual #47                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      41: iload_1
      42: invokevirtual #50                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      45: ldc           #52                 // String  not found
      47: invokevirtual #47                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      50: invokevirtual #56                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      53: invokevirtual #57                 // Method java/lang/Object.toString:()Ljava/lang/String;
      56: invokespecial #60                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
      59: athrow
      60: new           #62                 // class io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition
      63: dup
      64: iload_2
      65: aload_0
      66: invokevirtual #66                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState.getMatrixSize:()I
      69: idiv
      70: iload_2
      71: aload_0
      72: invokevirtual #66                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState.getMatrixSize:()I
      75: irem
      76: invokespecial #69                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition."<init>":(II)V
      79: areturn

  public static final java.util.List<io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState> getSuccessors(io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState);
    Code:
       0: aload_0
       1: ldc           #9                  // String <this>
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: new           #78                 // class java/util/ArrayList
       9: dup
      10: invokespecial #79                 // Method java/util/ArrayList."<init>":()V
      13: checkcast     #29                 // class java/util/List
      16: astore_1
      17: aload_0
      18: iconst_0
      19: invokestatic  #81                 // Method getPosition:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;I)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;
      22: astore_2
      23: aconst_null
      24: astore_3
      25: aload_2
      26: invokevirtual #84                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getRow:()I
      29: ifle          59
      32: aload_2
      33: aload_2
      34: invokevirtual #84                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getRow:()I
      37: iconst_1
      38: isub
      39: iconst_0
      40: iconst_2
      41: aconst_null
      42: invokestatic  #88                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.copy$default:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;IIILjava/lang/Object;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;
      45: astore_3
      46: aload_1
      47: aload_0
      48: aload_2
      49: aload_3
      50: invokestatic  #92                 // Method permuted:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;
      53: invokeinterface #96,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      58: pop
      59: aload_2
      60: invokevirtual #84                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getRow:()I
      63: aload_0
      64: invokevirtual #66                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState.getMatrixSize:()I
      67: iconst_1
      68: isub
      69: if_icmpge     99
      72: aload_2
      73: aload_2
      74: invokevirtual #84                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getRow:()I
      77: iconst_1
      78: iadd
      79: iconst_0
      80: iconst_2
      81: aconst_null
      82: invokestatic  #88                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.copy$default:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;IIILjava/lang/Object;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;
      85: astore_3
      86: aload_1
      87: aload_0
      88: aload_2
      89: aload_3
      90: invokestatic  #92                 // Method permuted:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;
      93: invokeinterface #96,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      98: pop
      99: aload_2
     100: invokevirtual #99                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getColumn:()I
     103: ifle          133
     106: aload_2
     107: iconst_0
     108: aload_2
     109: invokevirtual #99                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getColumn:()I
     112: iconst_1
     113: isub
     114: iconst_1
     115: aconst_null
     116: invokestatic  #88                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.copy$default:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;IIILjava/lang/Object;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;
     119: astore_3
     120: aload_1
     121: aload_0
     122: aload_2
     123: aload_3
     124: invokestatic  #92                 // Method permuted:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;
     127: invokeinterface #96,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
     132: pop
     133: aload_2
     134: invokevirtual #99                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getColumn:()I
     137: aload_0
     138: invokevirtual #66                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState.getMatrixSize:()I
     141: iconst_1
     142: isub
     143: if_icmpge     173
     146: aload_2
     147: iconst_0
     148: aload_2
     149: invokevirtual #99                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getColumn:()I
     152: iconst_1
     153: iadd
     154: iconst_1
     155: aconst_null
     156: invokestatic  #88                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.copy$default:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;IIILjava/lang/Object;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;
     159: astore_3
     160: aload_1
     161: aload_0
     162: aload_2
     163: aload_3
     164: invokestatic  #92                 // Method permuted:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;)Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;
     167: invokeinterface #96,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
     172: pop
     173: aload_1
     174: areturn

  public static final io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState permuted(io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState, io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition, io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition);
    Code:
       0: aload_0
       1: ldc           #9                  // String <this>
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #107                // String a
       9: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_2
      13: ldc           #109                // String b
      15: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      18: aload_0
      19: aload_1
      20: invokestatic  #112                // Method indexOf:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;)I
      23: istore_3
      24: aload_0
      25: aload_2
      26: invokestatic  #112                // Method indexOf:(Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState;Lio/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition;)I
      29: istore        4
      31: invokestatic  #117                // Method kotlin/collections/CollectionsKt.createListBuilder:()Ljava/util/List;
      34: astore        6
      36: aload         6
      38: astore        7
      40: iconst_0
      41: istore        8
      43: aload         7
      45: aload_0
      46: invokevirtual #21                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState.getTiles:()Ljava/util/List;
      49: checkcast     #119                // class java/util/Collection
      52: invokeinterface #123,  2          // InterfaceMethod java/util/List.addAll:(Ljava/util/Collection;)Z
      57: pop
      58: aload         7
      60: iload         4
      62: invokeinterface #127,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      67: checkcast     #129                // class java/lang/Number
      70: invokevirtual #132                // Method java/lang/Number.intValue:()I
      73: istore        9
      75: aload         7
      77: iload         4
      79: aload         7
      81: iload_3
      82: invokeinterface #127,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      87: invokeinterface #136,  3          // InterfaceMethod java/util/List.set:(ILjava/lang/Object;)Ljava/lang/Object;
      92: pop
      93: aload         7
      95: iload_3
      96: iload         9
      98: invokestatic  #27                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     101: invokeinterface #136,  3          // InterfaceMethod java/util/List.set:(ILjava/lang/Object;)Ljava/lang/Object;
     106: pop
     107: nop
     108: aload         6
     110: invokestatic  #140                // Method kotlin/collections/CollectionsKt.build:(Ljava/util/List;)Ljava/util/List;
     113: astore        5
     115: new           #17                 // class io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState
     118: dup
     119: aload         5
     121: invokespecial #143                // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState."<init>":(Ljava/util/List;)V
     124: areturn

  public static final int indexOf(io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState, io.github.andremion.slidingpuzzle.domain.puzzle.PuzzleState$TilePosition);
    Code:
       0: aload_0
       1: ldc           #9                  // String <this>
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: ldc           #151                // String position
       9: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      12: aload_1
      13: invokevirtual #84                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getRow:()I
      16: aload_0
      17: invokevirtual #66                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState.getMatrixSize:()I
      20: imul
      21: aload_1
      22: invokevirtual #99                 // Method io/github/andremion/slidingpuzzle/domain/puzzle/PuzzleState$TilePosition.getColumn:()I
      25: iadd
      26: ireturn
}


package com.marcdenning.adventofcode.day07

import java.io.File
import java.util.*
import java.util.regex.Pattern
import java.util.stream.Collectors

fun main(args: Array<String>) {
    var numberOfColors = 0

    Scanner(File(args[0])).use { scanner ->
        numberOfColors += getAncestorsOfTargetColor(parseRules(scanner), args[1]).size
    }
    println("Number of colors: $numberOfColors")
}

/**
 * Extract map of color to rule pairings from a Scanner. Rules are a map of color to number of bags.
 */
fun parseRules(scanner: Scanner): Map<String, Map<String, Int>> = mapOf(
    *
    scanner.findAll("([\\w\\s]+)\\sbags\\scontain\\s([\\d\\s\\w\\,]+)\\.\\n?").map {
        Pair(it.group(1), parseRulesForColor(it.group(2)))
    }.collect(Collectors.toList()).toTypedArray()
)

fun parseRulesForColor(rulesString: String): Map<String, Int> {
    if ("no other bags" == rulesString) {
        return emptyMap()
    }
    return mapOf(
        *rulesString.split(',')
            .map {
                val matcher = Pattern.compile("(\\d+)\\s([\\w\\s]+)\\sbags?").matcher(it.trim())

                matcher.matches()
                Pair(matcher.group(2), matcher.group(1).toInt())
            }.toTypedArray()
    )
}

fun getAncestorsOfTargetColor(rules: Map<String, Map<String, Int>>, targetColor: String): Set<String> {
    // find colors that immediately contain the target color
    val immediateParentRules = rules.filter { entry -> entry.value.containsKey(targetColor) }
    // recurse through map for immediate parent colors to find their parents
    if (immediateParentRules.isEmpty()) {
        return emptySet()
    }
    return immediateParentRules.keys union immediateParentRules.map { getAncestorsOfTargetColor(rules, it.key) }
        .reduce { s1, s2 -> s1 union s2}
}

Compiled from "Day07a.kt"
public final class com.marcdenning.adventofcode.day07.Day07aKt {
  public static final void main(java.lang.String[]);
    Code:
       0: aload_0
       1: ldc           #11                 // String args
       3: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: iconst_0
       7: istore_1
       8: new           #19                 // class java/util/Scanner
      11: dup
      12: new           #21                 // class java/io/File
      15: dup
      16: aload_0
      17: iconst_0
      18: aaload
      19: invokespecial #25                 // Method java/io/File."<init>":(Ljava/lang/String;)V
      22: invokespecial #28                 // Method java/util/Scanner."<init>":(Ljava/io/File;)V
      25: checkcast     #30                 // class java/io/Closeable
      28: astore_2
      29: aconst_null
      30: astore_3
      31: nop
      32: aload_2
      33: checkcast     #19                 // class java/util/Scanner
      36: astore        4
      38: iconst_0
      39: istore        5
      41: iload_1
      42: aload         4
      44: invokestatic  #34                 // Method parseRules:(Ljava/util/Scanner;)Ljava/util/Map;
      47: aload_0
      48: iconst_1
      49: aaload
      50: invokestatic  #38                 // Method getAncestorsOfTargetColor:(Ljava/util/Map;Ljava/lang/String;)Ljava/util/Set;
      53: invokeinterface #44,  1           // InterfaceMethod java/util/Set.size:()I
      58: iadd
      59: istore_1
      60: nop
      61: getstatic     #50                 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit;
      64: astore        4
      66: aload_2
      67: aload_3
      68: invokestatic  #56                 // Method kotlin/io/CloseableKt.closeFinally:(Ljava/io/Closeable;Ljava/lang/Throwable;)V
      71: goto          92
      74: astore        5
      76: aload         5
      78: astore_3
      79: aload         5
      81: athrow
      82: astore        5
      84: aload_2
      85: aload_3
      86: invokestatic  #56                 // Method kotlin/io/CloseableKt.closeFinally:(Ljava/io/Closeable;Ljava/lang/Throwable;)V
      89: aload         5
      91: athrow
      92: new           #58                 // class java/lang/StringBuilder
      95: dup
      96: invokespecial #61                 // Method java/lang/StringBuilder."<init>":()V
      99: ldc           #63                 // String Number of colors:
     101: invokevirtual #67                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     104: iload_1
     105: invokevirtual #70                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     108: invokevirtual #74                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     111: getstatic     #80                 // Field java/lang/System.out:Ljava/io/PrintStream;
     114: swap
     115: invokevirtual #86                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     118: return
    Exception table:
       from    to  target type
          31    66    74   Class java/lang/Throwable
          31    66    82   any
          74    82    82   any
          82    84    82   any

  public static final java.util.Map<java.lang.String, java.util.Map<java.lang.String, java.lang.Integer>> parseRules(java.util.Scanner);
    Code:
       0: aload_0
       1: ldc           #95                 // String scanner
       3: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: ldc           #97                 // String ([\\w\\s]+)\\sbags\\scontain\\s([\\d\\s\\w\\,]+)\\.\\n?
       9: invokevirtual #101                // Method java/util/Scanner.findAll:(Ljava/lang/String;)Ljava/util/stream/Stream;
      12: invokedynamic #120,  0            // InvokeDynamic #0:invoke:()Lkotlin/jvm/functions/Function1;
      17: invokedynamic #131,  0            // InvokeDynamic #1:apply:(Lkotlin/jvm/functions/Function1;)Ljava/util/function/Function;
      22: invokeinterface #137,  2          // InterfaceMethod java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
      27: invokestatic  #143                // Method java/util/stream/Collectors.toList:()Ljava/util/stream/Collector;
      30: invokeinterface #147,  2          // InterfaceMethod java/util/stream/Stream.collect:(Ljava/util/stream/Collector;)Ljava/lang/Object;
      35: astore_2
      36: aload_2
      37: ldc           #149                // String collect(...)
      39: invokestatic  #152                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
      42: aload_2
      43: checkcast     #154                // class java/util/Collection
      46: astore_2
      47: nop
      48: iconst_0
      49: istore_3
      50: aload_2
      51: astore        4
      53: aload         4
      55: iconst_0
      56: anewarray     #156                // class kotlin/Pair
      59: invokeinterface #160,  2          // InterfaceMethod java/util/Collection.toArray:([Ljava/lang/Object;)[Ljava/lang/Object;
      64: checkcast     #162                // class "[Lkotlin/Pair;"
      67: astore_1
      68: aload_1
      69: aload_1
      70: arraylength
      71: invokestatic  #168                // Method java/util/Arrays.copyOf:([Ljava/lang/Object;I)[Ljava/lang/Object;
      74: checkcast     #162                // class "[Lkotlin/Pair;"
      77: invokestatic  #174                // Method kotlin/collections/MapsKt.mapOf:([Lkotlin/Pair;)Ljava/util/Map;
      80: areturn

  public static final java.util.Map<java.lang.String, java.lang.Integer> parseRulesForColor(java.lang.String);
    Code:
       0: aload_0
       1: ldc           #183                // String rulesString
       3: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: ldc           #185                // String no other bags
       8: aload_0
       9: invokestatic  #189                // Method kotlin/jvm/internal/Intrinsics.areEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
      12: ifeq          19
      15: invokestatic  #193                // Method kotlin/collections/MapsKt.emptyMap:()Ljava/util/Map;
      18: areturn
      19: aload_0
      20: checkcast     #195                // class java/lang/CharSequence
      23: iconst_1
      24: newarray       char
      26: astore_2
      27: aload_2
      28: iconst_0
      29: bipush        44
      31: castore
      32: aload_2
      33: iconst_0
      34: iconst_0
      35: bipush        6
      37: aconst_null
      38: invokestatic  #201                // Method kotlin/text/StringsKt.split$default:(Ljava/lang/CharSequence;[CZIILjava/lang/Object;)Ljava/util/List;
      41: checkcast     #203                // class java/lang/Iterable
      44: astore_2
      45: nop
      46: iconst_0
      47: istore_3
      48: aload_2
      49: astore        4
      51: new           #205                // class java/util/ArrayList
      54: dup
      55: aload_2
      56: bipush        10
      58: invokestatic  #211                // Method kotlin/collections/CollectionsKt.collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
      61: invokespecial #214                // Method java/util/ArrayList."<init>":(I)V
      64: checkcast     #154                // class java/util/Collection
      67: astore        5
      69: iconst_0
      70: istore        6
      72: aload         4
      74: invokeinterface #218,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      79: astore        7
      81: aload         7
      83: invokeinterface #224,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      88: ifeq          188
      91: aload         7
      93: invokeinterface #228,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      98: astore        8
     100: aload         5
     102: aload         8
     104: checkcast     #230                // class java/lang/String
     107: astore        9
     109: astore        12
     111: iconst_0
     112: istore        10
     114: ldc           #232                // String (\\d+)\\s([\\w\\s]+)\\sbags?
     116: invokestatic  #238                // Method java/util/regex/Pattern.compile:(Ljava/lang/String;)Ljava/util/regex/Pattern;
     119: aload         9
     121: checkcast     #195                // class java/lang/CharSequence
     124: invokestatic  #242                // Method kotlin/text/StringsKt.trim:(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
     127: invokevirtual #243                // Method java/lang/Object.toString:()Ljava/lang/String;
     130: checkcast     #195                // class java/lang/CharSequence
     133: invokevirtual #247                // Method java/util/regex/Pattern.matcher:(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;
     136: astore        11
     138: aload         11
     140: invokevirtual #252                // Method java/util/regex/Matcher.matches:()Z
     143: pop
     144: new           #156                // class kotlin/Pair
     147: dup
     148: aload         11
     150: iconst_2
     151: invokevirtual #256                // Method java/util/regex/Matcher.group:(I)Ljava/lang/String;
     154: aload         11
     156: iconst_1
     157: invokevirtual #256                // Method java/util/regex/Matcher.group:(I)Ljava/lang/String;
     160: dup
     161: ldc_w         #258                // String group(...)
     164: invokestatic  #152                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
     167: invokestatic  #264                // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
     170: invokestatic  #268                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     173: invokespecial #271                // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
     176: aload         12
     178: swap
     179: invokeinterface #275,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     184: pop
     185: goto          81
     188: aload         5
     190: checkcast     #277                // class java/util/List
     193: nop
     194: checkcast     #154                // class java/util/Collection
     197: astore_2
     198: nop
     199: iconst_0
     200: istore_3
     201: aload_2
     202: astore        4
     204: aload         4
     206: iconst_0
     207: anewarray     #156                // class kotlin/Pair
     210: invokeinterface #160,  2          // InterfaceMethod java/util/Collection.toArray:([Ljava/lang/Object;)[Ljava/lang/Object;
     215: checkcast     #162                // class "[Lkotlin/Pair;"
     218: astore_1
     219: aload_1
     220: aload_1
     221: arraylength
     222: invokestatic  #168                // Method java/util/Arrays.copyOf:([Ljava/lang/Object;I)[Ljava/lang/Object;
     225: checkcast     #162                // class "[Lkotlin/Pair;"
     228: invokestatic  #174                // Method kotlin/collections/MapsKt.mapOf:([Lkotlin/Pair;)Ljava/util/Map;
     231: areturn

  public static final java.util.Set<java.lang.String> getAncestorsOfTargetColor(java.util.Map<java.lang.String, ? extends java.util.Map<java.lang.String, java.lang.Integer>>, java.lang.String);
    Code:
       0: aload_0
       1: ldc_w         #292                // String rules
       4: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       7: aload_1
       8: ldc_w         #294                // String targetColor
      11: invokestatic  #17                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
      14: aload_0
      15: astore_3
      16: iconst_0
      17: istore        4
      19: aload_3
      20: astore        5
      22: new           #296                // class java/util/LinkedHashMap
      25: dup
      26: invokespecial #297                // Method java/util/LinkedHashMap."<init>":()V
      29: checkcast     #299                // class java/util/Map
      32: astore        6
      34: iconst_0
      35: istore        7
      37: aload         5
      39: invokeinterface #303,  1          // InterfaceMethod java/util/Map.entrySet:()Ljava/util/Set;
      44: invokeinterface #304,  1          // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
      49: astore        8
      51: aload         8
      53: invokeinterface #224,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      58: ifeq          124
      61: aload         8
      63: invokeinterface #228,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      68: checkcast     #306                // class java/util/Map$Entry
      71: astore        9
      73: aload         9
      75: astore        10
      77: iconst_0
      78: istore        11
      80: aload         10
      82: invokeinterface #309,  1          // InterfaceMethod java/util/Map$Entry.getValue:()Ljava/lang/Object;
      87: checkcast     #299                // class java/util/Map
      90: aload_1
      91: invokeinterface #312,  2          // InterfaceMethod java/util/Map.containsKey:(Ljava/lang/Object;)Z
      96: ifeq          51
      99: aload         6
     101: aload         9
     103: invokeinterface #315,  1          // InterfaceMethod java/util/Map$Entry.getKey:()Ljava/lang/Object;
     108: aload         9
     110: invokeinterface #309,  1          // InterfaceMethod java/util/Map$Entry.getValue:()Ljava/lang/Object;
     115: invokeinterface #319,  3          // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
     120: pop
     121: goto          51
     124: aload         6
     126: nop
     127: astore_2
     128: aload_2
     129: invokeinterface #322,  1          // InterfaceMethod java/util/Map.isEmpty:()Z
     134: ifeq          141
     137: invokestatic  #327                // Method kotlin/collections/SetsKt.emptySet:()Ljava/util/Set;
     140: areturn
     141: aload_2
     142: invokeinterface #330,  1          // InterfaceMethod java/util/Map.keySet:()Ljava/util/Set;
     147: checkcast     #203                // class java/lang/Iterable
     150: aload_2
     151: astore_3
     152: astore        12
     154: iconst_0
     155: istore        4
     157: aload_3
     158: astore        5
     160: new           #205                // class java/util/ArrayList
     163: dup
     164: aload_3
     165: invokeinterface #331,  1          // InterfaceMethod java/util/Map.size:()I
     170: invokespecial #214                // Method java/util/ArrayList."<init>":(I)V
     173: checkcast     #154                // class java/util/Collection
     176: astore        6
     178: iconst_0
     179: istore        7
     181: aload         5
     183: invokeinterface #303,  1          // InterfaceMethod java/util/Map.entrySet:()Ljava/util/Set;
     188: invokeinterface #304,  1          // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
     193: astore        8
     195: aload         8
     197: invokeinterface #224,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     202: ifeq          254
     205: aload         8
     207: invokeinterface #228,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     212: checkcast     #306                // class java/util/Map$Entry
     215: astore        9
     217: aload         6
     219: aload         9
     221: astore        10
     223: astore        13
     225: iconst_0
     226: istore        11
     228: aload_0
     229: aload         10
     231: invokeinterface #315,  1          // InterfaceMethod java/util/Map$Entry.getKey:()Ljava/lang/Object;
     236: checkcast     #230                // class java/lang/String
     239: invokestatic  #38                 // Method getAncestorsOfTargetColor:(Ljava/util/Map;Ljava/lang/String;)Ljava/util/Set;
     242: aload         13
     244: swap
     245: invokeinterface #275,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     250: pop
     251: goto          195
     254: aload         6
     256: checkcast     #277                // class java/util/List
     259: nop
     260: aload         12
     262: swap
     263: checkcast     #203                // class java/lang/Iterable
     266: astore_3
     267: astore        12
     269: iconst_0
     270: istore        4
     272: aload_3
     273: invokeinterface #218,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     278: astore        5
     280: aload         5
     282: invokeinterface #224,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     287: ifne          301
     290: new           #333                // class java/lang/UnsupportedOperationException
     293: dup
     294: ldc_w         #335                // String Empty collection can\'t be reduced.
     297: invokespecial #336                // Method java/lang/UnsupportedOperationException."<init>":(Ljava/lang/String;)V
     300: athrow
     301: aload         5
     303: invokeinterface #228,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     308: astore        6
     310: aload         5
     312: invokeinterface #224,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
     317: ifeq          360
     320: aload         6
     322: aload         5
     324: invokeinterface #228,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     329: checkcast     #40                 // class java/util/Set
     332: astore        7
     334: checkcast     #40                 // class java/util/Set
     337: astore        8
     339: iconst_0
     340: istore        9
     342: aload         8
     344: checkcast     #203                // class java/lang/Iterable
     347: aload         7
     349: checkcast     #203                // class java/lang/Iterable
     352: invokestatic  #340                // Method kotlin/collections/CollectionsKt.union:(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set;
     355: astore        6
     357: goto          310
     360: aload         6
     362: aload         12
     364: swap
     365: checkcast     #203                // class java/lang/Iterable
     368: invokestatic  #340                // Method kotlin/collections/CollectionsKt.union:(Ljava/lang/Iterable;Ljava/lang/Iterable;)Ljava/util/Set;
     371: areturn

  private static final kotlin.Pair parseRules$lambda$1(java.util.regex.MatchResult);
    Code:
       0: new           #156                // class kotlin/Pair
       3: dup
       4: aload_0
       5: iconst_1
       6: invokeinterface #363,  2          // InterfaceMethod java/util/regex/MatchResult.group:(I)Ljava/lang/String;
      11: aload_0
      12: iconst_2
      13: invokeinterface #363,  2          // InterfaceMethod java/util/regex/MatchResult.group:(I)Ljava/lang/String;
      18: dup
      19: ldc_w         #258                // String group(...)
      22: invokestatic  #152                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
      25: invokestatic  #365                // Method parseRulesForColor:(Ljava/lang/String;)Ljava/util/Map;
      28: invokespecial #271                // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
      31: areturn

  private static final kotlin.Pair parseRules$lambda$2(kotlin.jvm.functions.Function1, java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: invokeinterface #370,  2          // InterfaceMethod kotlin/jvm/functions/Function1.invoke:(Ljava/lang/Object;)Ljava/lang/Object;
       7: checkcast     #156                // class kotlin/Pair
      10: areturn
}


package com.marcdenning.adventofcode.day23

import java.util.Collections.*

fun main(args: Array<String>) {
    var cups = args[0].map { Integer.parseInt(it + "") }
    val moves = args[1].toInt()

    for (i in 1..moves) {
        cups = moveCups(cups)
    }

    println(getCupsStateString(cups))
}

/**
 * Conduct moves for 1 round:
 *
 * 1. Pick up 3 cups that are immediately clockwise of the current cup. They are removed from the circle; cup spacing is adjusted as necessary to maintain the circle.
 * 2. Pick destination cup: the cup with a label equal to the current cup's label minus one. If this would select one of the cups that was just picked up, the crab will keep subtracting one until it finds a cup that wasn't just picked up. If at any point in this process the value goes below the lowest value on any cup's label, it wraps around to the highest value on any cup's label instead.
 * 3. Places the cups just picked up so that they are immediately clockwise of the destination cup. They keep the same order as when they were picked up.
 * 4. Select a new current cup: the cup which is immediately clockwise of the current cup.
 *
 * @param cups current state of cups, current cup is always index 0
 * @return new state of cups after a round
 */
fun moveCups(cups: List<Int>): List<Int> {
    val movedCups = mutableListOf(*cups.toTypedArray())
    val removedCups = mutableListOf<Int>()

    for (i in 1..3) {
        removedCups.add(movedCups.removeAt(1))
    }

    val destinationCupIndex = findDestinationCupIndex(movedCups)

    movedCups.addAll(destinationCupIndex + 1, removedCups)
    rotate(movedCups, -1)

    return movedCups
}

fun findDestinationCupIndex(cups: List<Int>): Int {
    val currentCup = cups[0]
    var destinationCup = currentCup - 1

    if (cups.contains(destinationCup)) {
        return cups.indexOf(destinationCup)
    }
    do {
        destinationCup--
        if (destinationCup < min(cups)) {
            destinationCup = max(cups)
        }
    } while (!cups.contains(destinationCup))
    return cups.indexOf(destinationCup)
}

fun getCupsStateString(cups: List<Int>): String {
    val assembledCups = mutableListOf(*cups.toTypedArray())
    val indexOf1 = assembledCups.indexOf(1)

    rotate(assembledCups, -indexOf1)
    assembledCups.remove(1)
    return assembledCups.joinToString(separator = "", transform = {i -> "" + i})
}

Compiled from "Day23a.kt"
public final class com.marcdenning.adventofcode.day23.Day23aKt {
  public static final void main(java.lang.String[]);
    Code:
       0: aload_0
       1: ldc           #9                  // String args
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: iconst_0
       8: aaload
       9: checkcast     #17                 // class java/lang/CharSequence
      12: astore_2
      13: iconst_0
      14: istore_3
      15: aload_2
      16: astore        4
      18: new           #19                 // class java/util/ArrayList
      21: dup
      22: aload_2
      23: invokeinterface #23,  1           // InterfaceMethod java/lang/CharSequence.length:()I
      28: invokespecial #27                 // Method java/util/ArrayList."<init>":(I)V
      31: checkcast     #29                 // class java/util/Collection
      34: astore        5
      36: iconst_0
      37: istore        6
      39: iconst_0
      40: istore        7
      42: iload         7
      44: aload         4
      46: invokeinterface #23,  1           // InterfaceMethod java/lang/CharSequence.length:()I
      51: if_icmpge     125
      54: aload         4
      56: iload         7
      58: invokeinterface #33,  2           // InterfaceMethod java/lang/CharSequence.charAt:(I)C
      63: istore        8
      65: aload         5
      67: iload         8
      69: istore        9
      71: astore        13
      73: iconst_0
      74: istore        10
      76: iload         9
      78: istore        11
      80: ldc           #35                 // String
      82: astore        12
      84: new           #37                 // class java/lang/StringBuilder
      87: dup
      88: invokespecial #40                 // Method java/lang/StringBuilder."<init>":()V
      91: iload         11
      93: invokevirtual #44                 // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
      96: aload         12
      98: invokevirtual #47                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     101: invokevirtual #51                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     104: invokestatic  #57                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
     107: invokestatic  #61                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     110: aload         13
     112: swap
     113: invokeinterface #65,  2           // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     118: pop
     119: iinc          7, 1
     122: goto          42
     125: aload         5
     127: checkcast     #67                 // class java/util/List
     130: nop
     131: astore_1
     132: aload_0
     133: iconst_1
     134: aaload
     135: invokestatic  #57                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
     138: istore_2
     139: iconst_1
     140: istore_3
     141: iload_3
     142: iload_2
     143: if_icmpgt     162
     146: aload_1
     147: invokestatic  #71                 // Method moveCups:(Ljava/util/List;)Ljava/util/List;
     150: astore_1
     151: iload_3
     152: iload_2
     153: if_icmpeq     162
     156: iinc          3, 1
     159: goto          146
     162: aload_1
     163: invokestatic  #75                 // Method getCupsStateString:(Ljava/util/List;)Ljava/lang/String;
     166: getstatic     #81                 // Field java/lang/System.out:Ljava/io/PrintStream;
     169: swap
     170: invokevirtual #87                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     173: return

  public static final java.util.List<java.lang.Integer> moveCups(java.util.List<java.lang.Integer>);
    Code:
       0: aload_0
       1: ldc           #107                // String cups
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #29                 // class java/util/Collection
      10: astore_3
      11: iconst_0
      12: istore        4
      14: aload_3
      15: astore        5
      17: aload         5
      19: iconst_0
      20: anewarray     #53                 // class java/lang/Integer
      23: invokeinterface #111,  2          // InterfaceMethod java/util/Collection.toArray:([Ljava/lang/Object;)[Ljava/lang/Object;
      28: checkcast     #113                // class "[Ljava/lang/Integer;"
      31: astore_2
      32: aload_2
      33: aload_2
      34: arraylength
      35: invokestatic  #119                // Method java/util/Arrays.copyOf:([Ljava/lang/Object;I)[Ljava/lang/Object;
      38: invokestatic  #125                // Method kotlin/collections/CollectionsKt.mutableListOf:([Ljava/lang/Object;)Ljava/util/List;
      41: astore_1
      42: new           #19                 // class java/util/ArrayList
      45: dup
      46: invokespecial #126                // Method java/util/ArrayList."<init>":()V
      49: checkcast     #67                 // class java/util/List
      52: astore_2
      53: iconst_1
      54: istore_3
      55: iload_3
      56: iconst_4
      57: if_icmpge     80
      60: aload_2
      61: aload_1
      62: iconst_1
      63: invokeinterface #130,  2          // InterfaceMethod java/util/List.remove:(I)Ljava/lang/Object;
      68: invokeinterface #131,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      73: pop
      74: iinc          3, 1
      77: goto          55
      80: aload_1
      81: invokestatic  #135                // Method findDestinationCupIndex:(Ljava/util/List;)I
      84: istore_3
      85: aload_1
      86: iload_3
      87: iconst_1
      88: iadd
      89: aload_2
      90: checkcast     #29                 // class java/util/Collection
      93: invokeinterface #139,  3          // InterfaceMethod java/util/List.addAll:(ILjava/util/Collection;)Z
      98: pop
      99: aload_1
     100: iconst_m1
     101: invokestatic  #145                // Method java/util/Collections.rotate:(Ljava/util/List;I)V
     104: aload_1
     105: areturn

  public static final int findDestinationCupIndex(java.util.List<java.lang.Integer>);
    Code:
       0: aload_0
       1: ldc           #107                // String cups
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: iconst_0
       8: invokeinterface #155,  2          // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      13: checkcast     #157                // class java/lang/Number
      16: invokevirtual #160                // Method java/lang/Number.intValue:()I
      19: istore_1
      20: iload_1
      21: iconst_1
      22: isub
      23: istore_2
      24: aload_0
      25: iload_2
      26: invokestatic  #61                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      29: invokeinterface #163,  2          // InterfaceMethod java/util/List.contains:(Ljava/lang/Object;)Z
      34: ifeq          48
      37: aload_0
      38: iload_2
      39: invokestatic  #61                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      42: invokeinterface #167,  2          // InterfaceMethod java/util/List.indexOf:(Ljava/lang/Object;)I
      47: ireturn
      48: iinc          2, -1
      51: iload_2
      52: aload_0
      53: checkcast     #29                 // class java/util/Collection
      56: invokestatic  #171                // Method java/util/Collections.min:(Ljava/util/Collection;)Ljava/lang/Object;
      59: checkcast     #157                // class java/lang/Number
      62: invokevirtual #160                // Method java/lang/Number.intValue:()I
      65: if_icmpge     88
      68: aload_0
      69: checkcast     #29                 // class java/util/Collection
      72: invokestatic  #174                // Method java/util/Collections.max:(Ljava/util/Collection;)Ljava/lang/Object;
      75: dup
      76: ldc           #176                // String max(...)
      78: invokestatic  #179                // Method kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue:(Ljava/lang/Object;Ljava/lang/String;)V
      81: checkcast     #157                // class java/lang/Number
      84: invokevirtual #160                // Method java/lang/Number.intValue:()I
      87: istore_2
      88: aload_0
      89: iload_2
      90: invokestatic  #61                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      93: invokeinterface #163,  2          // InterfaceMethod java/util/List.contains:(Ljava/lang/Object;)Z
      98: ifeq          48
     101: aload_0
     102: iload_2
     103: invokestatic  #61                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     106: invokeinterface #167,  2          // InterfaceMethod java/util/List.indexOf:(Ljava/lang/Object;)I
     111: ireturn

  public static final java.lang.String getCupsStateString(java.util.List<java.lang.Integer>);
    Code:
       0: aload_0
       1: ldc           #107                // String cups
       3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: checkcast     #29                 // class java/util/Collection
      10: astore_3
      11: iconst_0
      12: istore        4
      14: aload_3
      15: astore        5
      17: aload         5
      19: iconst_0
      20: anewarray     #53                 // class java/lang/Integer
      23: invokeinterface #111,  2          // InterfaceMethod java/util/Collection.toArray:([Ljava/lang/Object;)[Ljava/lang/Object;
      28: checkcast     #113                // class "[Ljava/lang/Integer;"
      31: astore_2
      32: aload_2
      33: aload_2
      34: arraylength
      35: invokestatic  #119                // Method java/util/Arrays.copyOf:([Ljava/lang/Object;I)[Ljava/lang/Object;
      38: invokestatic  #125                // Method kotlin/collections/CollectionsKt.mutableListOf:([Ljava/lang/Object;)Ljava/util/List;
      41: astore_1
      42: aload_1
      43: iconst_1
      44: invokestatic  #61                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      47: invokeinterface #167,  2          // InterfaceMethod java/util/List.indexOf:(Ljava/lang/Object;)I
      52: istore_2
      53: aload_1
      54: iload_2
      55: ineg
      56: invokestatic  #145                // Method java/util/Collections.rotate:(Ljava/util/List;I)V
      59: aload_1
      60: iconst_1
      61: invokestatic  #61                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      64: invokeinterface #184,  2          // InterfaceMethod java/util/List.remove:(Ljava/lang/Object;)Z
      69: pop
      70: aload_1
      71: checkcast     #186                // class java/lang/Iterable
      74: ldc           #35                 // String
      76: checkcast     #17                 // class java/lang/CharSequence
      79: aconst_null
      80: aconst_null
      81: iconst_0
      82: aconst_null
      83: invokedynamic #206,  0            // InvokeDynamic #0:invoke:()Lkotlin/jvm/functions/Function1;
      88: bipush        30
      90: aconst_null
      91: invokestatic  #210                // Method kotlin/collections/CollectionsKt.joinToString$default:(Ljava/lang/Iterable;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;ILjava/lang/CharSequence;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/String;
      94: areturn

  private static final java.lang.CharSequence getCupsStateString$lambda$1(int);
    Code:
       0: new           #37                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial #40                 // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #35                 // String
       9: invokevirtual #47                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: iload_0
      13: invokevirtual #215                // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      16: invokevirtual #51                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      19: checkcast     #17                 // class java/lang/CharSequence
      22: areturn
}