package com.learn.sudoku

import java.io.File

typealias Board = Array<Array<Int?>> // Row first. For example, b[1][3] means second row forth column.

fun main(args: Array<String>)
{
    val board = Sudoku.read(args[0])
    println(Sudoku.print(board))
    println((1..11).joinToString("") { "=" } )
    if(!Sudoku.solve(board)) println("Unsolvable.")
}

object Sudoku
{
    fun read(filename: String): Board =
            File(filename).useLines { lines ->
                lines.take(9).map { line -> line.map { if (it.isDigit()) it.toString().toInt() else null }.toTypedArray() }
                        .toList().toTypedArray()
            }

    fun print(board: Board): String =
        board.withIndex().joinToString("\n") { (i, r) ->
            val columnString = r.withIndex().joinToString("") { (ci, c) ->
                (if ((ci>0)&&(0==(ci%3))) "|" else "") + (c?.toString()?:" ")
            }
            (if ((i>0)&&(0==(i%3))) (1..11).joinToString("", postfix = "\n") { "-" } else "") + columnString
        }

    fun solve(board: Board): Boolean = solve(board, 0, 0)

    private fun solve(board: Board, row: Int, column: Int): Boolean
    {
        if (board[row][column] == null)
        {
            for (value in 1..9)
            {
                if (!violates(value, row, column, board))
                {
                    board[row][column] = value
                    if ( (row == 8) && (column == 8) )
                    {
                        println(print(board))
                        return true
                    }
                    else
                    {
                        val (nextRow, nextColumn) = next(row, column)
                        if (solve(board, nextRow, nextColumn))
                        {
                            return true
                        }
                    }
                    board[row][column] = null
                }
            }
            return false
        }
        else
        {
            if ( (row == 8) && (column == 8) )
            {
                println(print(board))
                return true
            }
            else
            {
                val (nextRow, nextColumn) = next(row, column)
                return solve(board, nextRow, nextColumn)
            }
        }
    }

    private fun next(row: Int, column: Int): Pair<Int, Int>
    {
        if (column < 8) return Pair(row, column + 1)
        else return Pair(row + 1, 0)
    }

    private fun violates(value: Int, row: Int, column: Int, board: Board): Boolean
    {
        return violatesRow(value, row, column, board) ||
                violatesColumn(value, row, column, board) ||
                violatesSquare(value, row, column, board)
    }

    private fun violatesRow(value: Int, row: Int, column: Int, board: Board): Boolean =
            (0 until 9).any { (it != column) && (board[row][it] == value) }

    private fun violatesColumn(value: Int, row: Int, column: Int, board: Board): Boolean =
            (0 until 9).any { (it != row) && (board[it][column] == value) }

    private fun violatesSquare(value: Int, row: Int, column: Int, board: Board): Boolean
    {
        val rowBegin = (row/3)*3
        val columnBegin = (column/3)*3
        for (r in rowBegin until (rowBegin+3))
        {
            for (c in columnBegin until (columnBegin+3))
            {
                if ( (r != row) && (c != column) )
                {
                    if (board[r][c] == value)
                    {
                        return true
                    }
                }
            }
        }
        return false
    }
}

Compiled from "Sudoku.kt"
public final class com.learn.sudoku.SudokuKt {
  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: getstatic     #21                 // Field com/learn/sudoku/Sudoku.INSTANCE:Lcom/learn/sudoku/Sudoku;
       9: aload_0
      10: iconst_0
      11: aaload
      12: invokevirtual #25                 // Method com/learn/sudoku/Sudoku.read:(Ljava/lang/String;)[[Ljava/lang/Integer;
      15: astore_1
      16: getstatic     #21                 // Field com/learn/sudoku/Sudoku.INSTANCE:Lcom/learn/sudoku/Sudoku;
      19: aload_1
      20: invokevirtual #29                 // Method com/learn/sudoku/Sudoku.print:([[Ljava/lang/Integer;)Ljava/lang/String;
      23: getstatic     #35                 // Field java/lang/System.out:Ljava/io/PrintStream;
      26: swap
      27: invokevirtual #41                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      30: new           #43                 // class kotlin/ranges/IntRange
      33: dup
      34: iconst_1
      35: bipush        11
      37: invokespecial #47                 // Method kotlin/ranges/IntRange."<init>":(II)V
      40: checkcast     #49                 // class java/lang/Iterable
      43: ldc           #51                 // String
      45: checkcast     #53                 // class java/lang/CharSequence
      48: aconst_null
      49: aconst_null
      50: iconst_0
      51: aconst_null
      52: invokedynamic #73,  0             // InvokeDynamic #0:invoke:()Lkotlin/jvm/functions/Function1;
      57: bipush        30
      59: aconst_null
      60: invokestatic  #79                 // 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;
      63: getstatic     #35                 // Field java/lang/System.out:Ljava/io/PrintStream;
      66: swap
      67: invokevirtual #41                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      70: getstatic     #21                 // Field com/learn/sudoku/Sudoku.INSTANCE:Lcom/learn/sudoku/Sudoku;
      73: aload_1
      74: invokevirtual #83                 // Method com/learn/sudoku/Sudoku.solve:([[Ljava/lang/Integer;)Z
      77: ifne          89
      80: ldc           #85                 // String Unsolvable.
      82: getstatic     #35                 // Field java/lang/System.out:Ljava/io/PrintStream;
      85: swap
      86: invokevirtual #41                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      89: return

  private static final java.lang.CharSequence main$lambda$0(int);
    Code:
       0: ldc           #91                 // String =
       2: checkcast     #53                 // class java/lang/CharSequence
       5: areturn
}

Compiled from "Sudoku.kt"
public final class com.learn.sudoku.Sudoku {
  public static final com.learn.sudoku.Sudoku INSTANCE;

  private com.learn.sudoku.Sudoku();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public final java.lang.Integer[][] read(java.lang.String);
    Code:
       0: aload_1
       1: ldc           #17                 // String filename
       3: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: new           #25                 // class java/io/File
       9: dup
      10: aload_1
      11: invokespecial #28                 // Method java/io/File."<init>":(Ljava/lang/String;)V
      14: astore_2
      15: getstatic     #34                 // Field kotlin/text/Charsets.UTF_8:Ljava/nio/charset/Charset;
      18: astore_3
      19: iconst_0
      20: istore        4
      22: aload_2
      23: astore        5
      25: sipush        8192
      28: istore        6
      30: aload         5
      32: astore        7
      34: new           #36                 // class java/io/InputStreamReader
      37: dup
      38: new           #38                 // class java/io/FileInputStream
      41: dup
      42: aload         7
      44: invokespecial #41                 // Method java/io/FileInputStream."<init>":(Ljava/io/File;)V
      47: checkcast     #43                 // class java/io/InputStream
      50: aload_3
      51: invokespecial #46                 // Method java/io/InputStreamReader."<init>":(Ljava/io/InputStream;Ljava/nio/charset/Charset;)V
      54: checkcast     #48                 // class java/io/Reader
      57: astore        7
      59: aload         7
      61: instanceof    #50                 // class java/io/BufferedReader
      64: ifeq          75
      67: aload         7
      69: checkcast     #50                 // class java/io/BufferedReader
      72: goto          86
      75: new           #50                 // class java/io/BufferedReader
      78: dup
      79: aload         7
      81: iload         6
      83: invokespecial #53                 // Method java/io/BufferedReader."<init>":(Ljava/io/Reader;I)V
      86: checkcast     #55                 // class java/io/Closeable
      89: astore        5
      91: aconst_null
      92: astore        6
      94: nop
      95: aload         5
      97: checkcast     #50                 // class java/io/BufferedReader
     100: astore        7
     102: iconst_0
     103: istore        8
     105: aload         7
     107: invokestatic  #61                 // Method kotlin/io/TextStreamsKt.lineSequence:(Ljava/io/BufferedReader;)Lkotlin/sequences/Sequence;
     110: astore        9
     112: iconst_0
     113: istore        10
     115: aload         9
     117: bipush        9
     119: invokestatic  #67                 // Method kotlin/sequences/SequencesKt.take:(Lkotlin/sequences/Sequence;I)Lkotlin/sequences/Sequence;
     122: invokedynamic #86,  0             // InvokeDynamic #0:invoke:()Lkotlin/jvm/functions/Function1;
     127: invokestatic  #90                 // Method kotlin/sequences/SequencesKt.map:(Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence;
     130: invokestatic  #94                 // Method kotlin/sequences/SequencesKt.toList:(Lkotlin/sequences/Sequence;)Ljava/util/List;
     133: checkcast     #96                 // class java/util/Collection
     136: astore        11
     138: iconst_0
     139: istore        12
     141: aload         11
     143: astore        13
     145: aload         13
     147: iconst_0
     148: anewarray     #98                 // class "[Ljava/lang/Integer;"
     151: invokeinterface #102,  2          // InterfaceMethod java/util/Collection.toArray:([Ljava/lang/Object;)[Ljava/lang/Object;
     156: checkcast     #104                // class "[[Ljava/lang/Integer;"
     159: nop
     160: astore        7
     162: aload         5
     164: aload         6
     166: invokestatic  #110                // Method kotlin/io/CloseableKt.closeFinally:(Ljava/io/Closeable;Ljava/lang/Throwable;)V
     169: aload         7
     171: goto          195
     174: astore        8
     176: aload         8
     178: astore        6
     180: aload         8
     182: athrow
     183: astore        8
     185: aload         5
     187: aload         6
     189: invokestatic  #110                // Method kotlin/io/CloseableKt.closeFinally:(Ljava/io/Closeable;Ljava/lang/Throwable;)V
     192: aload         8
     194: athrow
     195: nop
     196: checkcast     #104                // class "[[Ljava/lang/Integer;"
     199: areturn
    Exception table:
       from    to  target type
          94   162   174   Class java/lang/Throwable
          94   162   183   any
         174   183   183   any
         183   185   183   any

  public final java.lang.String print(java.lang.Integer[][]);
    Code:
       0: aload_1
       1: ldc           #136                // String board
       3: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_1
       7: checkcast     #138                // class "[Ljava/lang/Object;"
      10: invokestatic  #144                // Method kotlin/collections/ArraysKt.withIndex:([Ljava/lang/Object;)Ljava/lang/Iterable;
      13: ldc           #146                // String \n
      15: checkcast     #148                // class java/lang/CharSequence
      18: aconst_null
      19: aconst_null
      20: iconst_0
      21: aconst_null
      22: invokedynamic #155,  0            // InvokeDynamic #1:invoke:()Lkotlin/jvm/functions/Function1;
      27: bipush        30
      29: aconst_null
      30: invokestatic  #161                // 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;
      33: areturn

  public final boolean solve(java.lang.Integer[][]);
    Code:
       0: aload_1
       1: ldc           #136                // String board
       3: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: aload_0
       7: aload_1
       8: iconst_0
       9: iconst_0
      10: invokespecial #166                // Method solve:([[Ljava/lang/Integer;II)Z
      13: ireturn

  private final boolean solve(java.lang.Integer[][], int, int);
    Code:
       0: aload_1
       1: iload_2
       2: aaload
       3: iload_3
       4: aaload
       5: ifnonnull     128
       8: iconst_1
       9: istore        4
      11: iload         4
      13: bipush        10
      15: if_icmpge     126
      18: aload_0
      19: iload         4
      21: iload_2
      22: iload_3
      23: aload_1
      24: invokespecial #170                // Method violates:(III[[Ljava/lang/Integer;)Z
      27: ifne          120
      30: aload_1
      31: iload_2
      32: aaload
      33: iload_3
      34: iload         4
      36: invokestatic  #176                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      39: aastore
      40: iload_2
      41: bipush        8
      43: if_icmpne     66
      46: iload_3
      47: bipush        8
      49: if_icmpne     66
      52: aload_0
      53: aload_1
      54: invokevirtual #178                // Method print:([[Ljava/lang/Integer;)Ljava/lang/String;
      57: getstatic     #184                // Field java/lang/System.out:Ljava/io/PrintStream;
      60: swap
      61: invokevirtual #190                // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      64: iconst_1
      65: ireturn
      66: aload_0
      67: iload_2
      68: iload_3
      69: invokespecial #194                // Method next:(II)Lkotlin/Pair;
      72: astore        5
      74: aload         5
      76: invokevirtual #200                // Method kotlin/Pair.component1:()Ljava/lang/Object;
      79: checkcast     #202                // class java/lang/Number
      82: invokevirtual #206                // Method java/lang/Number.intValue:()I
      85: istore        6
      87: aload         5
      89: invokevirtual #209                // Method kotlin/Pair.component2:()Ljava/lang/Object;
      92: checkcast     #202                // class java/lang/Number
      95: invokevirtual #206                // Method java/lang/Number.intValue:()I
      98: istore        7
     100: aload_0
     101: aload_1
     102: iload         6
     104: iload         7
     106: invokespecial #166                // Method solve:([[Ljava/lang/Integer;II)Z
     109: ifeq          114
     112: iconst_1
     113: ireturn
     114: aload_1
     115: iload_2
     116: aaload
     117: iload_3
     118: aconst_null
     119: aastore
     120: iinc          4, 1
     123: goto          11
     126: iconst_0
     127: ireturn
     128: iload_2
     129: bipush        8
     131: if_icmpne     154
     134: iload_3
     135: bipush        8
     137: if_icmpne     154
     140: aload_0
     141: aload_1
     142: invokevirtual #178                // Method print:([[Ljava/lang/Integer;)Ljava/lang/String;
     145: getstatic     #184                // Field java/lang/System.out:Ljava/io/PrintStream;
     148: swap
     149: invokevirtual #190                // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     152: iconst_1
     153: ireturn
     154: aload_0
     155: iload_2
     156: iload_3
     157: invokespecial #194                // Method next:(II)Lkotlin/Pair;
     160: astore        4
     162: aload         4
     164: invokevirtual #200                // Method kotlin/Pair.component1:()Ljava/lang/Object;
     167: checkcast     #202                // class java/lang/Number
     170: invokevirtual #206                // Method java/lang/Number.intValue:()I
     173: istore        5
     175: aload         4
     177: invokevirtual #209                // Method kotlin/Pair.component2:()Ljava/lang/Object;
     180: checkcast     #202                // class java/lang/Number
     183: invokevirtual #206                // Method java/lang/Number.intValue:()I
     186: istore        6
     188: aload_0
     189: aload_1
     190: iload         5
     192: iload         6
     194: invokespecial #166                // Method solve:([[Ljava/lang/Integer;II)Z
     197: ireturn

  private final kotlin.Pair<java.lang.Integer, java.lang.Integer> next(int, int);
    Code:
       0: iload_2
       1: bipush        8
       3: if_icmpge     24
       6: new           #196                // class kotlin/Pair
       9: dup
      10: iload_1
      11: invokestatic  #176                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      14: iload_2
      15: iconst_1
      16: iadd
      17: invokestatic  #176                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      20: invokespecial #218                // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
      23: areturn
      24: new           #196                // class kotlin/Pair
      27: dup
      28: iload_1
      29: iconst_1
      30: iadd
      31: invokestatic  #176                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      34: iconst_0
      35: invokestatic  #176                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      38: invokespecial #218                // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
      41: areturn

  private final boolean violates(int, int, int, java.lang.Integer[][]);
    Code:
       0: aload_0
       1: iload_1
       2: iload_2
       3: iload_3
       4: aload         4
       6: invokespecial #221                // Method violatesRow:(III[[Ljava/lang/Integer;)Z
       9: ifne          36
      12: aload_0
      13: iload_1
      14: iload_2
      15: iload_3
      16: aload         4
      18: invokespecial #224                // Method violatesColumn:(III[[Ljava/lang/Integer;)Z
      21: ifne          36
      24: aload_0
      25: iload_1
      26: iload_2
      27: iload_3
      28: aload         4
      30: invokespecial #227                // Method violatesSquare:(III[[Ljava/lang/Integer;)Z
      33: ifeq          40
      36: iconst_1
      37: goto          41
      40: iconst_0
      41: ireturn

  private final boolean violatesRow(int, int, int, java.lang.Integer[][]);
    Code:
       0: iconst_0
       1: bipush        9
       3: invokestatic  #233                // Method kotlin/ranges/RangesKt.until:(II)Lkotlin/ranges/IntRange;
       6: checkcast     #235                // class java/lang/Iterable
       9: astore        5
      11: iconst_0
      12: istore        6
      14: aload         5
      16: instanceof    #96                 // class java/util/Collection
      19: ifeq          39
      22: aload         5
      24: checkcast     #96                 // class java/util/Collection
      27: invokeinterface #239,  1          // InterfaceMethod java/util/Collection.isEmpty:()Z
      32: ifeq          39
      35: iconst_0
      36: goto          120
      39: aload         5
      41: invokeinterface #243,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      46: astore        7
      48: aload         7
      50: invokeinterface #248,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      55: ifeq          119
      58: aload         7
      60: checkcast     #250                // class kotlin/collections/IntIterator
      63: invokevirtual #253                // Method kotlin/collections/IntIterator.nextInt:()I
      66: istore        8
      68: iload         8
      70: istore        9
      72: iconst_0
      73: istore        10
      75: iload         9
      77: iload_3
      78: if_icmpeq     111
      81: aload         4
      83: iload_2
      84: aaload
      85: iload         9
      87: aaload
      88: iload_1
      89: istore        11
      91: dup
      92: ifnonnull     99
      95: pop
      96: goto          111
      99: invokevirtual #254                // Method java/lang/Integer.intValue:()I
     102: iload         11
     104: if_icmpne     111
     107: iconst_1
     108: goto          112
     111: iconst_0
     112: ifeq          48
     115: iconst_1
     116: goto          120
     119: iconst_0
     120: ireturn

  private final boolean violatesColumn(int, int, int, java.lang.Integer[][]);
    Code:
       0: iconst_0
       1: bipush        9
       3: invokestatic  #233                // Method kotlin/ranges/RangesKt.until:(II)Lkotlin/ranges/IntRange;
       6: checkcast     #235                // class java/lang/Iterable
       9: astore        5
      11: iconst_0
      12: istore        6
      14: aload         5
      16: instanceof    #96                 // class java/util/Collection
      19: ifeq          39
      22: aload         5
      24: checkcast     #96                 // class java/util/Collection
      27: invokeinterface #239,  1          // InterfaceMethod java/util/Collection.isEmpty:()Z
      32: ifeq          39
      35: iconst_0
      36: goto          120
      39: aload         5
      41: invokeinterface #243,  1          // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
      46: astore        7
      48: aload         7
      50: invokeinterface #248,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      55: ifeq          119
      58: aload         7
      60: checkcast     #250                // class kotlin/collections/IntIterator
      63: invokevirtual #253                // Method kotlin/collections/IntIterator.nextInt:()I
      66: istore        8
      68: iload         8
      70: istore        9
      72: iconst_0
      73: istore        10
      75: iload         9
      77: iload_2
      78: if_icmpeq     111
      81: aload         4
      83: iload         9
      85: aaload
      86: iload_3
      87: aaload
      88: iload_1
      89: istore        11
      91: dup
      92: ifnonnull     99
      95: pop
      96: goto          111
      99: invokevirtual #254                // Method java/lang/Integer.intValue:()I
     102: iload         11
     104: if_icmpne     111
     107: iconst_1
     108: goto          112
     111: iconst_0
     112: ifeq          48
     115: iconst_1
     116: goto          120
     119: iconst_0
     120: ireturn

  private final boolean violatesSquare(int, int, int, java.lang.Integer[][]);
    Code:
       0: iload_2
       1: iconst_3
       2: idiv
       3: iconst_3
       4: imul
       5: istore        5
       7: iload_3
       8: iconst_3
       9: idiv
      10: iconst_3
      11: imul
      12: istore        6
      14: iload         5
      16: istore        7
      18: iload         5
      20: iconst_3
      21: iadd
      22: istore        8
      24: iload         7
      26: iload         8
      28: if_icmpge     101
      31: iload         6
      33: istore        9
      35: iload         6
      37: iconst_3
      38: iadd
      39: istore        10
      41: iload         9
      43: iload         10
      45: if_icmpge     95
      48: iload         7
      50: iload_2
      51: if_icmpeq     89
      54: iload         9
      56: iload_3
      57: if_icmpeq     89
      60: aload         4
      62: iload         7
      64: aaload
      65: iload         9
      67: aaload
      68: iload_1
      69: istore        11
      71: dup
      72: ifnonnull     79
      75: pop
      76: goto          89
      79: invokevirtual #254                // Method java/lang/Integer.intValue:()I
      82: iload         11
      84: if_icmpne     89
      87: iconst_1
      88: ireturn
      89: iinc          9, 1
      92: goto          41
      95: iinc          7, 1
      98: goto          24
     101: iconst_0
     102: ireturn

  private static final java.lang.Integer[] read$lambda$2$lambda$1(java.lang.String);
    Code:
       0: aload_0
       1: ldc_w         #267                // String line
       4: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       7: aload_0
       8: checkcast     #148                // class java/lang/CharSequence
      11: astore_1
      12: iconst_0
      13: istore_2
      14: aload_1
      15: astore_3
      16: new           #269                // class java/util/ArrayList
      19: dup
      20: aload_1
      21: invokeinterface #272,  1          // InterfaceMethod java/lang/CharSequence.length:()I
      26: invokespecial #275                // Method java/util/ArrayList."<init>":(I)V
      29: checkcast     #96                 // class java/util/Collection
      32: astore        4
      34: iconst_0
      35: istore        5
      37: iconst_0
      38: istore        6
      40: iload         6
      42: aload_3
      43: invokeinterface #272,  1          // InterfaceMethod java/lang/CharSequence.length:()I
      48: if_icmpge     110
      51: aload_3
      52: iload         6
      54: invokeinterface #279,  2          // InterfaceMethod java/lang/CharSequence.charAt:(I)C
      59: istore        7
      61: aload         4
      63: iload         7
      65: istore        8
      67: astore        10
      69: iconst_0
      70: istore        9
      72: iload         8
      74: invokestatic  #285                // Method java/lang/Character.isDigit:(C)Z
      77: ifeq          94
      80: iload         8
      82: invokestatic  #288                // Method java/lang/String.valueOf:(C)Ljava/lang/String;
      85: invokestatic  #292                // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
      88: invokestatic  #176                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      91: goto          95
      94: aconst_null
      95: aload         10
      97: swap
      98: invokeinterface #296,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     103: pop
     104: iinc          6, 1
     107: goto          40
     110: aload         4
     112: checkcast     #298                // class java/util/List
     115: nop
     116: checkcast     #96                 // class java/util/Collection
     119: astore_1
     120: nop
     121: iconst_0
     122: istore_2
     123: aload_1
     124: astore_3
     125: aload_3
     126: iconst_0
     127: anewarray     #172                // class java/lang/Integer
     130: invokeinterface #102,  2          // InterfaceMethod java/util/Collection.toArray:([Ljava/lang/Object;)[Ljava/lang/Object;
     135: checkcast     #98                 // class "[Ljava/lang/Integer;"
     138: areturn

  private static final java.lang.CharSequence print$lambda$5$lambda$3(kotlin.collections.IndexedValue);
    Code:
       0: aload_0
       1: ldc_w         #310                // String <destruct>
       4: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       7: aload_0
       8: invokevirtual #314                // Method kotlin/collections/IndexedValue.component1:()I
      11: istore_1
      12: aload_0
      13: invokevirtual #315                // Method kotlin/collections/IndexedValue.component2:()Ljava/lang/Object;
      16: checkcast     #172                // class java/lang/Integer
      19: astore_2
      20: new           #317                // class java/lang/StringBuilder
      23: dup
      24: invokespecial #318                // Method java/lang/StringBuilder."<init>":()V
      27: iload_1
      28: ifle          43
      31: iload_1
      32: iconst_3
      33: irem
      34: ifne          43
      37: ldc_w         #320                // String |
      40: goto          46
      43: ldc_w         #322                // String
      46: invokevirtual #326                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      49: aload_2
      50: dup
      51: ifnull        61
      54: invokevirtual #330                // Method java/lang/Integer.toString:()Ljava/lang/String;
      57: dup
      58: ifnonnull     65
      61: pop
      62: ldc_w         #332                // String
      65: invokevirtual #326                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      68: invokevirtual #333                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      71: checkcast     #148                // class java/lang/CharSequence
      74: areturn

  private static final java.lang.CharSequence print$lambda$5$lambda$4(int);
    Code:
       0: ldc_w         #339                // String -
       3: checkcast     #148                // class java/lang/CharSequence
       6: areturn

  private static final java.lang.CharSequence print$lambda$5(kotlin.collections.IndexedValue);
    Code:
       0: aload_0
       1: ldc_w         #310                // String <destruct>
       4: invokestatic  #23                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       7: aload_0
       8: invokevirtual #314                // Method kotlin/collections/IndexedValue.component1:()I
      11: istore_1
      12: aload_0
      13: invokevirtual #315                // Method kotlin/collections/IndexedValue.component2:()Ljava/lang/Object;
      16: checkcast     #98                 // class "[Ljava/lang/Integer;"
      19: astore_2
      20: aload_2
      21: invokestatic  #144                // Method kotlin/collections/ArraysKt.withIndex:([Ljava/lang/Object;)Ljava/lang/Iterable;
      24: ldc_w         #322                // String
      27: checkcast     #148                // class java/lang/CharSequence
      30: aconst_null
      31: aconst_null
      32: iconst_0
      33: aconst_null
      34: invokedynamic #343,  0            // InvokeDynamic #2:invoke:()Lkotlin/jvm/functions/Function1;
      39: bipush        30
      41: aconst_null
      42: invokestatic  #161                // 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;
      45: astore_3
      46: new           #317                // class java/lang/StringBuilder
      49: dup
      50: invokespecial #318                // Method java/lang/StringBuilder."<init>":()V
      53: iload_1
      54: ifle          104
      57: iload_1
      58: iconst_3
      59: irem
      60: ifne          104
      63: new           #345                // class kotlin/ranges/IntRange
      66: dup
      67: iconst_1
      68: bipush        11
      70: invokespecial #348                // Method kotlin/ranges/IntRange."<init>":(II)V
      73: checkcast     #235                // class java/lang/Iterable
      76: ldc_w         #322                // String
      79: checkcast     #148                // class java/lang/CharSequence
      82: aconst_null
      83: ldc           #146                // String \n
      85: checkcast     #148                // class java/lang/CharSequence
      88: iconst_0
      89: aconst_null
      90: invokedynamic #354,  0            // InvokeDynamic #3:invoke:()Lkotlin/jvm/functions/Function1;
      95: bipush        26
      97: aconst_null
      98: invokestatic  #161                // 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;
     101: goto          107
     104: ldc_w         #322                // String
     107: invokevirtual #326                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     110: aload_3
     111: invokevirtual #326                // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     114: invokevirtual #333                // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     117: checkcast     #148                // class java/lang/CharSequence
     120: areturn

  static {};
    Code:
       0: new           #2                  // class com/learn/sudoku/Sudoku
       3: dup
       4: invokespecial #358                // Method "<init>":()V
       7: putstatic     #361                // Field INSTANCE:Lcom/learn/sudoku/Sudoku;
      10: return
}


package homeworks.homework7.task2.model

object MinimaxAlgorithm {
    private const val MAX_VALUE = 1000
    private const val MIN_VALUE = -MAX_VALUE
    private const val BASIC_VALUE = 10

    fun getBestMove(board: Array<CharArray>, playerSign: Char, opponentSign: Char, emptySign: Char): Pair<Int, Int> {
        var bestValue = MIN_VALUE
        var bestMove = Pair(-1, -1)
        for (i in 0..2) {
            for (j in 0..2) {
                if (board[i][j] == emptySign) {
                    board[i][j] = playerSign
                    val moveValue = minimax(board, false, playerSign, opponentSign, emptySign)
                    val move = Pair(i, j)
                    board[i][j] = emptySign // Undo
                    bestMove = move.takeIf { bestValue < moveValue } ?: bestMove
                    bestValue = moveValue.takeIf { bestValue < it } ?: bestValue
                }
            }
        }
        return bestMove
    }

    private fun minimax(
        board: Array<CharArray>,
        isPlayer: Boolean, // !isPlayer = isOpponent
        playerSign: Char,
        opponentSign: Char,
        emptySign: Char
    ): Int {
        val score = evaluateCurrentState(board, playerSign, opponentSign)

        return if (score == BASIC_VALUE) {
            score
        } else if (score == -BASIC_VALUE) {
            score
        } else if (!isMovesLeft(board, emptySign)) {
            0
        } else if (isPlayer) {
            playerHandling(board, isPlayer, playerSign, opponentSign, emptySign)
        } else {
            opponentHandling(board, isPlayer, playerSign, opponentSign, emptySign)
        }
    }

    private fun playerHandling(
        board: Array<CharArray>,
        isPlayer: Boolean,
        playerSign: Char,
        opponentSign: Char,
        emptySign: Char
    ): Int {
        var best = MIN_VALUE
        for (i in 0..2) {
            for (j in 0..2) {
                if (board[i][j] == emptySign) {
                    board[i][j] = playerSign
                    best = best.coerceAtLeast(minimax(board, !isPlayer, playerSign, opponentSign, emptySign))
                    board[i][j] = emptySign // Undo
                }
            }
        }
        return best
    }

    private fun opponentHandling(
        board: Array<CharArray>,
        isPlayer: Boolean,
        playerSign: Char,
        opponentSign: Char,
        emptySign: Char
    ): Int {
        var best = MAX_VALUE
        for (i in 0..2) {
            for (j in 0..2) {
                if (board[i][j] == emptySign) {
                    board[i][j] = opponentSign
                    best = best.coerceAtMost(minimax(board, !isPlayer, playerSign, opponentSign, emptySign))
                    board[i][j] = emptySign // Undo
                }
            }
        }
        return best
    }

    private fun isMovesLeft(board: Array<CharArray>, emptySign: Char): Boolean {
        for (raw in board) {
            for (elem in raw) {
                if (elem == emptySign) {
                    return true
                }
            }
        }
        return false
    }

    private fun evaluateCurrentState(board: Array<CharArray>, playerSign: Char, opponentSign: Char): Int {
        val results = emptyList<Int>().toMutableList()
        results.add(checkRowsForWinningCombinations(board, playerSign, opponentSign))
        results.add(checkColumnsForWinningCombinations(board, playerSign, opponentSign))
        results.add(checkMainDiagonalForWinningCombination(board, playerSign, opponentSign))
        results.add(checkAntidiagonalForWinningCombination(board, playerSign, opponentSign))
        for (result in results) {
            if (result == BASIC_VALUE || result == -BASIC_VALUE) {
                return result
            }
        }
        return 0
    }

    /*
        The following 4 functions return:
            BASIC_VALUE - when player wins;
            -BASIC_VALUE - when opponent wins;
            0 - when tie.
     */
    private fun checkRowsForWinningCombinations(board: Array<CharArray>, playerSign: Char, opponentSign: Char): Int {
        for (row in 0..2) {
            if (board[row][0] == board[row][1] && board[row][1] == board[row][2]) {
                return if (board[row][0] == playerSign) {
                    BASIC_VALUE
                } else if (board[row][0] == opponentSign) {
                    -BASIC_VALUE
                } else continue
            }
        }
        return 0
    }

    private fun checkColumnsForWinningCombinations(board: Array<CharArray>, playerSign: Char, opponentSign: Char): Int {
        for (column in 0..2) {
            if (board[0][column] == board[1][column] && board[1][column] == board[2][column]) {
                return if (board[0][column] == playerSign) {
                    BASIC_VALUE
                } else if (board[0][column] == opponentSign) {
                    -BASIC_VALUE
                } else continue
            }
        }
        return 0
    }

    private fun checkMainDiagonalForWinningCombination(
        board: Array<CharArray>,
        playerSign: Char,
        opponentSign: Char
    ): Int {
        if (board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
            return when (board[1][1]) {
                playerSign -> BASIC_VALUE
                opponentSign -> -BASIC_VALUE
                else -> 0
            }
        }
        return 0
    }

    private fun checkAntidiagonalForWinningCombination(
        board: Array<CharArray>,
        playerSign: Char,
        opponentSign: Char
    ): Int {
        if (board[0][2] == board[1][1] && board[1][1] == board[2][0]) {
            return when (board[1][1]) {
                playerSign -> BASIC_VALUE
                opponentSign -> -BASIC_VALUE
                else -> 0
            }
        }
        return 0
    }
}

Compiled from "MinimaxAlgorithm.kt"
public final class homeworks.homework7.task2.model.MinimaxAlgorithm {
  public static final homeworks.homework7.task2.model.MinimaxAlgorithm INSTANCE;

  private static final int MAX_VALUE;

  private static final int MIN_VALUE;

  private static final int BASIC_VALUE;

  private homeworks.homework7.task2.model.MinimaxAlgorithm();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public final kotlin.Pair<java.lang.Integer, java.lang.Integer> getBestMove(char[][], char, char, char);
    Code:
       0: aload_1
       1: ldc           #16                 // String board
       3: invokestatic  #22                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: iconst_0
       7: istore        5
       9: sipush        -1000
      12: istore        5
      14: new           #24                 // class kotlin/Pair
      17: dup
      18: iconst_m1
      19: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      22: iconst_m1
      23: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      26: invokespecial #33                 // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
      29: astore        6
      31: iconst_0
      32: istore        7
      34: iload         7
      36: iconst_3
      37: if_icmpge     218
      40: iconst_0
      41: istore        8
      43: iload         8
      45: iconst_3
      46: if_icmpge     212
      49: aload_1
      50: iload         7
      52: aaload
      53: iload         8
      55: caload
      56: iload         4
      58: if_icmpne     206
      61: aload_1
      62: iload         7
      64: aaload
      65: iload         8
      67: iload_2
      68: castore
      69: aload_0
      70: aload_1
      71: iconst_0
      72: iload_2
      73: iload_3
      74: iload         4
      76: invokespecial #37                 // Method minimax:([[CZCCC)I
      79: istore        9
      81: new           #24                 // class kotlin/Pair
      84: dup
      85: iload         7
      87: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      90: iload         8
      92: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      95: invokespecial #33                 // Method kotlin/Pair."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
      98: astore        10
     100: aload_1
     101: iload         7
     103: aaload
     104: iload         8
     106: iload         4
     108: castore
     109: aload         10
     111: astore        11
     113: aload         11
     115: astore        12
     117: iconst_0
     118: istore        13
     120: iload         5
     122: iload         9
     124: if_icmpge     131
     127: iconst_1
     128: goto          132
     131: iconst_0
     132: ifeq          140
     135: aload         11
     137: goto          141
     140: aconst_null
     141: dup
     142: ifnonnull     148
     145: pop
     146: aload         6
     148: astore        6
     150: iload         9
     152: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     155: astore        11
     157: aload         11
     159: checkcast     #39                 // class java/lang/Number
     162: invokevirtual #43                 // Method java/lang/Number.intValue:()I
     165: istore        12
     167: iconst_0
     168: istore        13
     170: iload         5
     172: iload         12
     174: if_icmpge     181
     177: iconst_1
     178: goto          182
     181: iconst_0
     182: ifeq          190
     185: aload         11
     187: goto          191
     190: aconst_null
     191: dup
     192: ifnull        201
     195: invokevirtual #44                 // Method java/lang/Integer.intValue:()I
     198: goto          204
     201: pop
     202: iload         5
     204: istore        5
     206: iinc          8, 1
     209: goto          43
     212: iinc          7, 1
     215: goto          34
     218: aload         6
     220: areturn

  private final int minimax(char[][], boolean, char, char, char);
    Code:
       0: aload_0
       1: aload_1
       2: iload_3
       3: iload         4
       5: invokespecial #65                 // Method evaluateCurrentState:([[CCC)I
       8: istore        6
      10: iload         6
      12: bipush        10
      14: if_icmpne     22
      17: iload         6
      19: goto          77
      22: iload         6
      24: bipush        -10
      26: if_icmpne     34
      29: iload         6
      31: goto          77
      34: aload_0
      35: aload_1
      36: iload         5
      38: invokespecial #69                 // Method isMovesLeft:([[CC)Z
      41: ifne          48
      44: iconst_0
      45: goto          77
      48: iload_2
      49: ifeq          66
      52: aload_0
      53: aload_1
      54: iload_2
      55: iload_3
      56: iload         4
      58: iload         5
      60: invokespecial #72                 // Method playerHandling:([[CZCCC)I
      63: goto          77
      66: aload_0
      67: aload_1
      68: iload_2
      69: iload_3
      70: iload         4
      72: iload         5
      74: invokespecial #75                 // Method opponentHandling:([[CZCCC)I
      77: ireturn

  private final int playerHandling(char[][], boolean, char, char, char);
    Code:
       0: sipush        -1000
       3: istore        6
       5: iconst_0
       6: istore        7
       8: iload         7
      10: iconst_3
      11: if_icmpge     90
      14: iconst_0
      15: istore        8
      17: iload         8
      19: iconst_3
      20: if_icmpge     84
      23: aload_1
      24: iload         7
      26: aaload
      27: iload         8
      29: caload
      30: iload         5
      32: if_icmpne     78
      35: aload_1
      36: iload         7
      38: aaload
      39: iload         8
      41: iload_3
      42: castore
      43: iload         6
      45: aload_0
      46: aload_1
      47: iload_2
      48: ifne          55
      51: iconst_1
      52: goto          56
      55: iconst_0
      56: iload_3
      57: iload         4
      59: iload         5
      61: invokespecial #37                 // Method minimax:([[CZCCC)I
      64: invokestatic  #84                 // Method kotlin/ranges/RangesKt.coerceAtLeast:(II)I
      67: istore        6
      69: aload_1
      70: iload         7
      72: aaload
      73: iload         8
      75: iload         5
      77: castore
      78: iinc          8, 1
      81: goto          17
      84: iinc          7, 1
      87: goto          8
      90: iload         6
      92: ireturn

  private final int opponentHandling(char[][], boolean, char, char, char);
    Code:
       0: sipush        1000
       3: istore        6
       5: iconst_0
       6: istore        7
       8: iload         7
      10: iconst_3
      11: if_icmpge     91
      14: iconst_0
      15: istore        8
      17: iload         8
      19: iconst_3
      20: if_icmpge     85
      23: aload_1
      24: iload         7
      26: aaload
      27: iload         8
      29: caload
      30: iload         5
      32: if_icmpne     79
      35: aload_1
      36: iload         7
      38: aaload
      39: iload         8
      41: iload         4
      43: castore
      44: iload         6
      46: aload_0
      47: aload_1
      48: iload_2
      49: ifne          56
      52: iconst_1
      53: goto          57
      56: iconst_0
      57: iload_3
      58: iload         4
      60: iload         5
      62: invokespecial #37                 // Method minimax:([[CZCCC)I
      65: invokestatic  #88                 // Method kotlin/ranges/RangesKt.coerceAtMost:(II)I
      68: istore        6
      70: aload_1
      71: iload         7
      73: aaload
      74: iload         8
      76: iload         5
      78: castore
      79: iinc          8, 1
      82: goto          17
      85: iinc          7, 1
      88: goto          8
      91: iload         6
      93: ireturn

  private final boolean isMovesLeft(char[][], char);
    Code:
       0: iconst_0
       1: istore_3
       2: aload_1
       3: checkcast     #90                 // class "[Ljava/lang/Object;"
       6: arraylength
       7: istore        4
       9: iload_3
      10: iload         4
      12: if_icmpge     62
      15: aload_1
      16: iload_3
      17: aaload
      18: astore        5
      20: iconst_0
      21: istore        6
      23: aload         5
      25: arraylength
      26: istore        7
      28: iload         6
      30: iload         7
      32: if_icmpge     56
      35: aload         5
      37: iload         6
      39: caload
      40: istore        8
      42: iload         8
      44: iload_2
      45: if_icmpne     50
      48: iconst_1
      49: ireturn
      50: iinc          6, 1
      53: goto          28
      56: iinc          3, 1
      59: goto          9
      62: iconst_0
      63: ireturn

  private final int evaluateCurrentState(char[][], char, char);
    Code:
       0: invokestatic  #100                // Method kotlin/collections/CollectionsKt.emptyList:()Ljava/util/List;
       3: checkcast     #102                // class java/util/Collection
       6: invokestatic  #106                // Method kotlin/collections/CollectionsKt.toMutableList:(Ljava/util/Collection;)Ljava/util/List;
       9: astore        4
      11: aload         4
      13: aload_0
      14: aload_1
      15: iload_2
      16: iload_3
      17: invokespecial #109                // Method checkRowsForWinningCombinations:([[CCC)I
      20: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      23: invokeinterface #115,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      28: pop
      29: aload         4
      31: aload_0
      32: aload_1
      33: iload_2
      34: iload_3
      35: invokespecial #118                // Method checkColumnsForWinningCombinations:([[CCC)I
      38: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      41: invokeinterface #115,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      46: pop
      47: aload         4
      49: aload_0
      50: aload_1
      51: iload_2
      52: iload_3
      53: invokespecial #121                // Method checkMainDiagonalForWinningCombination:([[CCC)I
      56: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      59: invokeinterface #115,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      64: pop
      65: aload         4
      67: aload_0
      68: aload_1
      69: iload_2
      70: iload_3
      71: invokespecial #124                // Method checkAntidiagonalForWinningCombination:([[CCC)I
      74: invokestatic  #30                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      77: invokeinterface #115,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      82: pop
      83: aload         4
      85: invokeinterface #128,  1          // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      90: astore        5
      92: aload         5
      94: invokeinterface #134,  1          // InterfaceMethod java/util/Iterator.hasNext:()Z
      99: ifeq          150
     102: aload         5
     104: invokeinterface #138,  1          // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     109: checkcast     #39                 // class java/lang/Number
     112: invokevirtual #43                 // Method java/lang/Number.intValue:()I
     115: istore        6
     117: iload         6
     119: lookupswitch  { // 2
                   -10: 144
                    10: 144
               default: 147
          }
     144: iload         6
     146: ireturn
     147: goto          92
     150: iconst_0
     151: ireturn

  private final int checkRowsForWinningCombinations(char[][], char, char);
    Code:
       0: iconst_0
       1: istore        4
       3: iload         4
       5: iconst_3
       6: if_icmpge     79
       9: aload_1
      10: iload         4
      12: aaload
      13: iconst_0
      14: caload
      15: aload_1
      16: iload         4
      18: aaload
      19: iconst_1
      20: caload
      21: if_icmpne     73
      24: aload_1
      25: iload         4
      27: aaload
      28: iconst_1
      29: caload
      30: aload_1
      31: iload         4
      33: aaload
      34: iconst_2
      35: caload
      36: if_icmpne     73
      39: aload_1
      40: iload         4
      42: aaload
      43: iconst_0
      44: caload
      45: iload_2
      46: if_icmpne     54
      49: bipush        10
      51: goto          72
      54: aload_1
      55: iload         4
      57: aaload
      58: iconst_0
      59: caload
      60: iload_3
      61: if_icmpne     69
      64: bipush        -10
      66: goto          72
      69: goto          73
      72: ireturn
      73: iinc          4, 1
      76: goto          3
      79: iconst_0
      80: ireturn

  private final int checkColumnsForWinningCombinations(char[][], char, char);
    Code:
       0: iconst_0
       1: istore        4
       3: iload         4
       5: iconst_3
       6: if_icmpge     79
       9: aload_1
      10: iconst_0
      11: aaload
      12: iload         4
      14: caload
      15: aload_1
      16: iconst_1
      17: aaload
      18: iload         4
      20: caload
      21: if_icmpne     73
      24: aload_1
      25: iconst_1
      26: aaload
      27: iload         4
      29: caload
      30: aload_1
      31: iconst_2
      32: aaload
      33: iload         4
      35: caload
      36: if_icmpne     73
      39: aload_1
      40: iconst_0
      41: aaload
      42: iload         4
      44: caload
      45: iload_2
      46: if_icmpne     54
      49: bipush        10
      51: goto          72
      54: aload_1
      55: iconst_0
      56: aaload
      57: iload         4
      59: caload
      60: iload_3
      61: if_icmpne     69
      64: bipush        -10
      66: goto          72
      69: goto          73
      72: ireturn
      73: iinc          4, 1
      76: goto          3
      79: iconst_0
      80: ireturn

  private final int checkMainDiagonalForWinningCombination(char[][], char, char);
    Code:
       0: aload_1
       1: iconst_0
       2: aaload
       3: iconst_0
       4: caload
       5: aload_1
       6: iconst_1
       7: aaload
       8: iconst_1
       9: caload
      10: if_icmpne     57
      13: aload_1
      14: iconst_1
      15: aaload
      16: iconst_1
      17: caload
      18: aload_1
      19: iconst_2
      20: aaload
      21: iconst_2
      22: caload
      23: if_icmpne     57
      26: aload_1
      27: iconst_1
      28: aaload
      29: iconst_1
      30: caload
      31: istore        4
      33: iload         4
      35: iload_2
      36: if_icmpne     44
      39: bipush        10
      41: goto          56
      44: iload         4
      46: iload_3
      47: if_icmpne     55
      50: bipush        -10
      52: goto          56
      55: iconst_0
      56: ireturn
      57: iconst_0
      58: ireturn

  private final int checkAntidiagonalForWinningCombination(char[][], char, char);
    Code:
       0: aload_1
       1: iconst_0
       2: aaload
       3: iconst_2
       4: caload
       5: aload_1
       6: iconst_1
       7: aaload
       8: iconst_1
       9: caload
      10: if_icmpne     57
      13: aload_1
      14: iconst_1
      15: aaload
      16: iconst_1
      17: caload
      18: aload_1
      19: iconst_2
      20: aaload
      21: iconst_0
      22: caload
      23: if_icmpne     57
      26: aload_1
      27: iconst_1
      28: aaload
      29: iconst_1
      30: caload
      31: istore        4
      33: iload         4
      35: iload_2
      36: if_icmpne     44
      39: bipush        10
      41: goto          56
      44: iload         4
      46: iload_3
      47: if_icmpne     55
      50: bipush        -10
      52: goto          56
      55: iconst_0
      56: ireturn
      57: iconst_0
      58: ireturn

  static {};
    Code:
       0: new           #2                  // class homeworks/homework7/task2/model/MinimaxAlgorithm
       3: dup
       4: invokespecial #145                // Method "<init>":()V
       7: putstatic     #148                // Field INSTANCE:Lhomeworks/homework7/task2/model/MinimaxAlgorithm;
      10: return
}


package com.nschulzke

enum class Stars {
    One, Two, Three;
}

private val mancalaIndices = setOf(6, 13)

data class Puzzle(
    private val board: List<Int>,
    private val turns: Int,
    private val targets: List<Pair<Stars, Int>>,
    val steps: List<Int> = emptyList(),
) {
    fun onBottom(pit: Int): Boolean =
        pit in 0..5

    fun onTop(pit: Int): Boolean =
        pit in 7..12

    fun opposite(pit: Int): Int =
        when (pit) {
            0 -> 12
            1 -> 11
            2 -> 10
            3 -> 9
            4 -> 8
            5 -> 7
            7 -> 5
            8 -> 4
            9 -> 3
            10 -> 2
            11 -> 1
            12 -> 0
            else -> throw IllegalArgumentException("Pit $pit has no opposite")
        }

    fun move(pit: Int): Puzzle {
        if (board[pit] == 0) {
            throw Error("Cannot move empty pit")
        }
        if (pit in mancalaIndices) {
            throw Error("Cannot move mancala")
        }
        val mutableBoard = board.toMutableList()
        val stones = mutableBoard[pit]
        mutableBoard[pit] = 0
        var index = pit
        for (i in 1..stones) {
            index = (index + 1) % mutableBoard.size
            mutableBoard[index]++
        }
        // If the final pit is opposite of another pit that is not empty, capture all pieces
        if (index !in mancalaIndices) {
            val oppositeIndex = opposite(index)
            val mancala = if (onBottom(index)) {
                6
            } else {
                13
            }
            if (mutableBoard[index] == 1 && mutableBoard[oppositeIndex] > 0) {
                mutableBoard[mancala] += mutableBoard[oppositeIndex] + 1
                mutableBoard[index] = 0
                mutableBoard[oppositeIndex] = 0
            }
        }
        return this.copy(
            board = mutableBoard,
            turns = if (index !in mancalaIndices) {
                turns - 1
            } else {
                turns
            },
            steps = steps + pit
        )
    }

    private fun pitToString(index: Int): String =
        board[index].toString().padStart(2, ' ')

    fun score(): Int =
        board[6] + board[13]

    fun starRating(): Stars? =
        targets.lastOrNull { score() >= it.second }?.first

    fun isComplete(): Boolean =
        turns <= 0 || starRating() == Stars.Three

    fun nonEmptyPitIndices(): Iterable<Int> =
        board.indices.filter { board[it] > 0 && !mancalaIndices.contains(it) }

    override fun toString(): String {
        return """
            |   ${pitToString(12)} ${pitToString(11)} ${pitToString(10)} ${pitToString(9)} ${pitToString(8)} ${pitToString(7)}
            |${pitToString(13)}                   ${pitToString(6)}
            |   ${pitToString(0)} ${pitToString(1)} ${pitToString(2)} ${pitToString(3)} ${pitToString(4)} ${pitToString(5)}
            |${turns} turns left
        """.trimMargin()
    }
}

class Solver {
    // Use a search tree to find the minimum number of moves to get 3 stars' worth of stones in the mancalas.
    fun solve(startingPuzzle: Puzzle): Puzzle? {
        val queue = mutableListOf(startingPuzzle)
        val visited = mutableSetOf(startingPuzzle)
        while (queue.isNotEmpty()) {
            val puzzle = queue.removeAt(0)
            if (puzzle.isComplete()) {
                if (puzzle.starRating() == Stars.Three) {
                    return puzzle
                }
            } else {
                for (pit in puzzle.nonEmptyPitIndices()) {
                    val nextGame = puzzle.move(pit)
                    if (nextGame !in visited) {
                        queue.add(nextGame)
                        visited.add(nextGame)
                    }
                }
            }
        }
        return null
    }

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val puzzle = Puzzle(
                board = mutableListOf(
                    1, 0, 3, 0, 4, 0, 0,
                    1, 0, 0, 0, 2, 0, 0,
                ),
                turns = 3,
                targets = listOf(
                    Stars.One to 8,
                    Stars.Two to 9,
                    Stars.Three to 10,
                ),
            )
            println(puzzle)
            val solver = Solver()
            val solution = solver.solve(puzzle)
            if (solution == null) {
                println("No solution found.")
            } else {
                solution.steps.fold(puzzle) { acc, next ->
                    acc.move(next).also { println("\nAfter moving $next:\n$it") }
                }
            }
        }
    }
}

Compiled from "Main.kt"
public final class com.nschulzke.MainKt {
  private static final java.util.Set<java.lang.Integer> mancalaIndices;

  public static final java.util.Set access$getMancalaIndices$p();
    Code:
       0: getstatic     #10                 // Field mancalaIndices:Ljava/util/Set;
       3: areturn

  static {};
    Code:
       0: iconst_2
       1: anewarray     #14                 // class java/lang/Integer
       4: astore_0
       5: aload_0
       6: iconst_0
       7: bipush        6
       9: invokestatic  #18                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      12: aastore
      13: aload_0
      14: iconst_1
      15: bipush        13
      17: invokestatic  #18                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      20: aastore
      21: aload_0
      22: invokestatic  #24                 // Method kotlin/collections/SetsKt.setOf:([Ljava/lang/Object;)Ljava/util/Set;
      25: putstatic     #10                 // Field mancalaIndices:Ljava/util/Set;
      28: return
}


package adventofcode.day04

fun main(args: Array<String>) {
    if (args.size < 2) {
        System.err.println("Usage: adventofcode day04 <START> <END>")
        System.exit(1)
    }

    val start = args[0].toInt()
    val end = args[1].toInt()

    println("Computing passwords in range $start - $end")

    val part1Passwords = (start..end).filter { isValidPassword(it, true) }
    println("[Part 1] Number of valid passwords: ${part1Passwords.size}")
    val part2Passwords = (start..end).filter { isValidPassword(it, false) }
    // for(invalidPassword in part1Passwords.minus(part2Passwords)) {
    //     println("[Part 2] Invalid: $invalidPassword")
    // }
    // for(validPassword in part2Passwords) {
    //     println("[Part 2] Valid: $validPassword")
    // }
    println("[Part 2] Number of valid passwords: ${part2Passwords.size}")
}

fun Int.iterateDigits(): List<Int> {
    var number = this
    return generateSequence {
        if (number == 0) {
            null
        } else {
            val digit = number % 10
            number = number / 10
            digit
        }
    }.toList().reversed()
}

fun isValidPassword(candidate: Int, allowMoreThanTwoDigitSequence: Boolean): Boolean {
    var last = -1
    var activeSequenceLength = 0
    var hasValidSequence = false
    for (digit in candidate.iterateDigits()) {
        if (last >= 0) {
            if (last > digit) {
                // Digits must be ascending
                return false;
            }

            if (allowMoreThanTwoDigitSequence) {
                if(last == digit) {
                    hasValidSequence = true
                }
            }
            else if (last != digit) {
                if (activeSequenceLength == 2) {
                    // Found a sequence of exactly two digits
                    hasValidSequence = true
                }
                activeSequenceLength = 0
            }
        }
        activeSequenceLength += 1
        last = digit
    }

    return hasValidSequence || activeSequenceLength == 2
}

Compiled from "main.kt"
public final class adventofcode.day04.MainKt {
  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: arraylength
       8: iconst_2
       9: if_icmpge     24
      12: getstatic     #21                 // Field java/lang/System.err:Ljava/io/PrintStream;
      15: ldc           #23                 // String Usage: adventofcode day04 <START> <END>
      17: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: iconst_1
      21: invokestatic  #33                 // Method java/lang/System.exit:(I)V
      24: aload_0
      25: iconst_0
      26: aaload
      27: invokestatic  #39                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
      30: istore_1
      31: aload_0
      32: iconst_1
      33: aaload
      34: invokestatic  #39                 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
      37: istore_2
      38: new           #41                 // class java/lang/StringBuilder
      41: dup
      42: invokespecial #45                 // Method java/lang/StringBuilder."<init>":()V
      45: ldc           #47                 // String Computing passwords in range
      47: invokevirtual #51                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      50: iload_1
      51: invokevirtual #54                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      54: ldc           #56                 // String  -
      56: invokevirtual #51                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      59: iload_2
      60: invokevirtual #54                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      63: invokevirtual #60                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      66: getstatic     #63                 // Field java/lang/System.out:Ljava/io/PrintStream;
      69: swap
      70: invokevirtual #66                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      73: new           #68                 // class kotlin/ranges/IntRange
      76: dup
      77: iload_1
      78: iload_2
      79: invokespecial #71                 // Method kotlin/ranges/IntRange."<init>":(II)V
      82: checkcast     #73                 // class java/lang/Iterable
      85: astore        4
      87: iconst_0
      88: istore        5
      90: aload         4
      92: astore        6
      94: new           #75                 // class java/util/ArrayList
      97: dup
      98: invokespecial #76                 // Method java/util/ArrayList."<init>":()V
     101: checkcast     #78                 // class java/util/Collection
     104: astore        7
     106: iconst_0
     107: istore        8
     109: aload         6
     111: invokeinterface #82,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     116: astore        9
     118: aload         9
     120: invokeinterface #88,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     125: ifeq          172
     128: aload         9
     130: invokeinterface #92,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     135: astore        10
     137: aload         10
     139: checkcast     #94                 // class java/lang/Number
     142: invokevirtual #98                 // Method java/lang/Number.intValue:()I
     145: istore        11
     147: iconst_0
     148: istore        12
     150: iload         11
     152: iconst_1
     153: invokestatic  #102                // Method isValidPassword:(IZ)Z
     156: ifeq          118
     159: aload         7
     161: aload         10
     163: invokeinterface #106,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     168: pop
     169: goto          118
     172: aload         7
     174: checkcast     #108                // class java/util/List
     177: nop
     178: astore_3
     179: new           #41                 // class java/lang/StringBuilder
     182: dup
     183: invokespecial #45                 // Method java/lang/StringBuilder."<init>":()V
     186: ldc           #110                // String [Part 1] Number of valid passwords:
     188: invokevirtual #51                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     191: aload_3
     192: invokeinterface #113,  1          // InterfaceMethod java/util/List.size:()I
     197: invokevirtual #54                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     200: invokevirtual #60                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     203: getstatic     #63                 // Field java/lang/System.out:Ljava/io/PrintStream;
     206: swap
     207: invokevirtual #66                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     210: new           #68                 // class kotlin/ranges/IntRange
     213: dup
     214: iload_1
     215: iload_2
     216: invokespecial #71                 // Method kotlin/ranges/IntRange."<init>":(II)V
     219: checkcast     #73                 // class java/lang/Iterable
     222: astore        5
     224: iconst_0
     225: istore        6
     227: aload         5
     229: astore        7
     231: new           #75                 // class java/util/ArrayList
     234: dup
     235: invokespecial #76                 // Method java/util/ArrayList."<init>":()V
     238: checkcast     #78                 // class java/util/Collection
     241: astore        8
     243: iconst_0
     244: istore        9
     246: aload         7
     248: invokeinterface #82,  1           // InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
     253: astore        10
     255: aload         10
     257: invokeinterface #88,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
     262: ifeq          309
     265: aload         10
     267: invokeinterface #92,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
     272: astore        11
     274: aload         11
     276: checkcast     #94                 // class java/lang/Number
     279: invokevirtual #98                 // Method java/lang/Number.intValue:()I
     282: istore        12
     284: iconst_0
     285: istore        13
     287: iload         12
     289: iconst_0
     290: invokestatic  #102                // Method isValidPassword:(IZ)Z
     293: ifeq          255
     296: aload         8
     298: aload         11
     300: invokeinterface #106,  2          // InterfaceMethod java/util/Collection.add:(Ljava/lang/Object;)Z
     305: pop
     306: goto          255
     309: aload         8
     311: checkcast     #108                // class java/util/List
     314: nop
     315: astore        4
     317: new           #41                 // class java/lang/StringBuilder
     320: dup
     321: invokespecial #45                 // Method java/lang/StringBuilder."<init>":()V
     324: ldc           #115                // String [Part 2] Number of valid passwords:
     326: invokevirtual #51                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     329: aload         4
     331: invokeinterface #113,  1          // InterfaceMethod java/util/List.size:()I
     336: invokevirtual #54                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
     339: invokevirtual #60                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     342: getstatic     #63                 // Field java/lang/System.out:Ljava/io/PrintStream;
     345: swap
     346: invokevirtual #66                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     349: return

  public static final java.util.List<java.lang.Integer> iterateDigits(int);
    Code:
       0: new           #140                // class kotlin/jvm/internal/Ref$IntRef
       3: dup
       4: invokespecial #141                // Method kotlin/jvm/internal/Ref$IntRef."<init>":()V
       7: astore_1
       8: aload_1
       9: iload_0
      10: putfield      #144                // Field kotlin/jvm/internal/Ref$IntRef.element:I
      13: aload_1
      14: invokedynamic #163,  0            // InvokeDynamic #0:invoke:(Lkotlin/jvm/internal/Ref$IntRef;)Lkotlin/jvm/functions/Function0;
      19: invokestatic  #169                // Method kotlin/sequences/SequencesKt.generateSequence:(Lkotlin/jvm/functions/Function0;)Lkotlin/sequences/Sequence;
      22: invokestatic  #173                // Method kotlin/sequences/SequencesKt.toList:(Lkotlin/sequences/Sequence;)Ljava/util/List;
      25: checkcast     #73                 // class java/lang/Iterable
      28: invokestatic  #179                // Method kotlin/collections/CollectionsKt.reversed:(Ljava/lang/Iterable;)Ljava/util/List;
      31: areturn

  public static final boolean isValidPassword(int, boolean);
    Code:
       0: iconst_m1
       1: istore_2
       2: iconst_0
       3: istore_3
       4: iconst_0
       5: istore        4
       7: iload_0
       8: invokestatic  #184                // Method iterateDigits:(I)Ljava/util/List;
      11: invokeinterface #185,  1          // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      16: astore        5
      18: aload         5
      20: invokeinterface #88,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
      25: ifeq          96
      28: aload         5
      30: invokeinterface #92,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      35: checkcast     #94                 // class java/lang/Number
      38: invokevirtual #98                 // Method java/lang/Number.intValue:()I
      41: istore        6
      43: iload_2
      44: iflt          87
      47: iload_2
      48: iload         6
      50: if_icmple     55
      53: iconst_0
      54: ireturn
      55: iload_1
      56: ifeq          71
      59: iload_2
      60: iload         6
      62: if_icmpne     87
      65: iconst_1
      66: istore        4
      68: goto          87
      71: iload_2
      72: iload         6
      74: if_icmpeq     87
      77: iload_3
      78: iconst_2
      79: if_icmpne     85
      82: iconst_1
      83: istore        4
      85: iconst_0
      86: istore_3
      87: iinc          3, 1
      90: iload         6
      92: istore_2
      93: goto          18
      96: iload         4
      98: ifne          106
     101: iload_3
     102: iconst_2
     103: if_icmpne     110
     106: iconst_1
     107: goto          111
     110: iconst_0
     111: ireturn

  private static final java.lang.Integer iterateDigits$lambda$2(kotlin.jvm.internal.Ref$IntRef);
    Code:
       0: aload_0
       1: getfield      #144                // Field kotlin/jvm/internal/Ref$IntRef.element:I
       4: ifne          11
       7: aconst_null
       8: goto          34
      11: aload_0
      12: getfield      #144                // Field kotlin/jvm/internal/Ref$IntRef.element:I
      15: bipush        10
      17: irem
      18: istore_1
      19: aload_0
      20: aload_0
      21: getfield      #144                // Field kotlin/jvm/internal/Ref$IntRef.element:I
      24: bipush        10
      26: idiv
      27: putfield      #144                // Field kotlin/jvm/internal/Ref$IntRef.element:I
      30: iload_1
      31: invokestatic  #196                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      34: areturn
}