3

I am new to kotlin . I am trying to create simple tic tac toe game app.

In many tutorial i have seen most of them using if statement to find the winners.So i tried to modify with predefiend values and check them with players input.


\\ this funtion checks the winner , where player1 and player2 are arraylist.
\\ example : player1 = [1,2,8] and player2 = [4,5,6]

fun checkWinner(player1,player2){

   var possibleCombos = arrayListOf(
            arrayListOf(1,2,3), arrayListOf(4,5,6), arrayListOf(7,8,9),
            arrayListOf(1,4,7), arrayListOf(2,5,8), arrayListOf(3,6,9),
            arrayListOf(7,5,3), arrayListOf(1,5,9))

   for(items in possibleCombos) {

            if(a.containsAll(items)) {

               Toast.makeText(this,"Winner is A $a",Toast.LENGTH_LONG).show()

               }
            else if (b.containsAll(items)) {

               Toast.makeText(this,"Winner is B $b",Toast.LENGTH_LONG).show()

              }
            else {

                Toast.makeText(this,"Game is tie",Toast.LENGTH_SHORT).show()

            }
        }

    }

It is not working and the else part is executing always. I want to toast the result.

Any solution?

Markus Kauppinen
  • 3,025
  • 4
  • 20
  • 30

3 Answers3

2

Well you're doing it the wrong way, you are checking that each element in that list is actually equal to the either player1 or player2.

So if you execute, [4,5,6] then you'll get 8 toasts in which 1 of them will be winner as checked against [4,5,6] while the other 7 will point to the else condition because [4,5,6] is not [1,2,3], [7,8,9], etc.

val possibleCombos = sequenceOf( // if want listOf, just add .asSequence() before map below, this way will break the mapping if result is obtained.
    listOf(1, 2, 3), listOf(4, 5, 6), listOf(7, 8, 9),
    listOf(1, 4, 7), listOf(2, 5, 8), listOf(3, 6, 9),
    listOf(7, 5, 3), listOf(1, 5, 9))

fun checkWinner(player1: List<Int>, player2: List<Int>) {
    // this will break the mapping once first non-null value is found, in terminal operation firstOrNull()
    val message = possibleCombos.map {
        when {
            player1.containsAll(it) -> "Winner is A $player1"
            player2.containsAll(it) -> "Winner is B $player2"
            else -> null
        }
    }.filterNotNull().firstOrNull() ?: "Game is tie"
    Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}

// Call this function (test sample)
checkWinner(arrayListOf(1, 2, 8, 3), arrayListOf(5, 4, 6, 7)) // may change to listOf as well

Result:

Winner is B [5, 4, 6, 7]

PS: I refactored arrayListOf() to listOf(), because that is what Kotlin-stdlib recommends to use. However you can switch back to the arrayListOf() as well

Animesh Sahu
  • 7,445
  • 2
  • 21
  • 49
1

I found this https://stackoverflow.com/a/35275418/8514508:

In Kotlin 1.1 you can use contentEquals and contentDeepEquals to compare two arrays for structural equality. e.g.:

a contentEquals b // true
b contentEquals c // false

In Kotlin 1.0 there are no "built-in functions to the Kotlin std-lib that tests two arrays for (value) equality for each element."

"Arrays are always compared using equals(), as all other objects" (Feedback Request: Limitations on Data Classes | Kotlin Blog).

So a.equals(b) will only return true if a and b reference the same array.

You can, however, create your own "optionals"-friendly methods using extension functions. e.g.:

fun Array<*>.equalsArray(other: Array<*>) = Arrays.equals(this, other)
fun Array<*>.deepEqualsArray(other: Array<*>) = Arrays.deepEquals(this, other)
Andres Rivas
  • 162
  • 3
  • 1
    This is not the issue. The OP is using `ArrayList`, not `Array`. And anyway, the OP is comparing the values of the player to each inner list individually using `containsAll`. So they are being compared `Int` to `Int`. – Tenfour04 May 08 '20 at 13:13
1

When you iterate the list, you need to break out of the loop once you've found a winner. And the else branch needs to be moved outside the loop, or else it will be reached by any win condition that wasn't the win condition for this game.

fun checkWinner(player1: List<Int>, player2: List<Int>){

    val possibleCombos = arrayListOf(
            arrayListOf(1,2,3), arrayListOf(4,5,6), arrayListOf(7,8,9),
            arrayListOf(1,4,7), arrayListOf(2,5,8), arrayListOf(3,6,9),
            arrayListOf(7,5,3), arrayListOf(1,5,9))

    var winner: List<Int>? = null
    for(items in possibleCombos) {
        if(player1.containsAll(items)) {
            winner = player1
            break
        }
        if(player2.containsAll(items)) {
            winner = player2
            break
        }
    }
    // If no winner was found, winner will still be null

    val resultText = when (winner) {
        player1 -> "Winner is A $player1"
        player2 -> "Winner is B $player2"
        else -> "Game is tie"
    }
    Toast.makeText(this, resultText, Toast.LENGTH_LONG).show()
}

Alternatively, you can use higher order functions instead of looping yourself to keep it concise:

val winner = when {
    possibleCombos.any { player1.containsAll(it) } -> player1
    possibleCombos.any { player2.containsAll(it) } -> player2
    else -> null
}
Tenfour04
  • 83,111
  • 11
  • 94
  • 154