Search code examples
javaandroidtic-tac-toe

Java Tic tac toe AI element not working


So, trying to make a 'simple' tic-tac-toe app that matches user input with an 'AI' that picks (just at random), empty cells to enter as an entity to play against. The problem I am having is that the true/false switch that I am using (in playerClick) to decide whos turn it is, does not seem to be working. It remains true throughout. This means and (I believe is the cause of), my AI's method, aiPlayerPick which is tied to randomButtonPick, is not being run, only the input from the human user is ever entered. My question then, is where is my logic falling apart here?

I think that's clear enough without babbling on too much. Any clarification needed, please just ask.

Thanks.

package com.example.richardcurteis.connect3;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;
import java.util.Random;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    boolean noughtsTurn;
    ArrayList board;
    Players player;
    String aiPickedButton;

    public void receiveClick(View view) {
        String buttonPressed = String.valueOf(view.getTag());
        board.remove(buttonPressed);
        System.out.println(board);
        System.out.println("receiveClick: " + noughtsTurn); // Is noughtsTurn true or false?
        humanPlayerTurn(view);
        aiPlayerTurn(view);

    }

    public void humanPlayerTurn(View view) {
        playerClick(view);
        noughtsTurn = false; // Set noughtsTurn to false for next player to have their turn
    }
   public void aiPlayerTurn(View view) {
       aiPickedButton = randomButtonPick();
       playerClick(view);
       noughtsTurn = true; // Set noughtsTurn to true for first player to start again
   }

    public void playerClick(View view) {
        Button B;
        if (view instanceof Button) {
            B = (Button) view;
            if ( noughtsTurn ) {
                B.setText(player.noughtsPlayer());
                B.setEnabled(false);
            } else {
                aiPlayerPick(); // This is where the AI process should mirror human input
            }
        }
    }

    public String randomButtonPick() {
        Random randomNumber = new Random();
        String arrayItem = (String) board.get(randomNumber.nextInt(board.size()));
        return arrayItem;
    }

    public Button aiPlayerPick() {
        Button btn = null;
        TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);

        for (int rowIndex = 0; rowIndex < tableLayout.getChildCount(); rowIndex++) {
            View tableLayoutChild = tableLayout.getChildAt(rowIndex);
            if (tableLayoutChild instanceof TableRow) {
                for (int i = 0; i < ((ViewGroup) tableLayoutChild).getChildCount(); i++) {
                    View view = ((ViewGroup) tableLayoutChild).getChildAt(i);
                    if (view instanceof Button && view.getTag() == aiPickedButton) {

                        View btn_v = view.findViewWithTag(aiPickedButton);
                        btn = (Button) btn_v;
                        btn.setText(player.crossesPlayer());
                        btn.setEnabled(false);
                        break;
                    } else {
                        i++;
                    }
                }
            }
        }
        return btn;
    }

    public class Players {
        public String noughtsPlayer() { return "O"; }
        public String crossesPlayer() { return "X"; }
        //public String blankButton() { return ""; } // Will be implemented eventually to help clear board
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        boolean noughtsTurn = true;
        System.out.println("Start: " + noughtsTurn);
        board = new ArrayList();
        for (int x = 0; x < getBoardSize(); x++) {
            String y = String.valueOf(x);
            board.add(y);
        }
        player = new Players();

    }

    public int getBoardSize() {
        int buttonCount = 0;
        TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);

        for (int rowIndex = 0; rowIndex < tableLayout.getChildCount(); rowIndex++) {
            View tableLayoutChild = tableLayout.getChildAt(rowIndex);
            if (tableLayoutChild instanceof TableRow) {
                for (int i = 0; i < ((ViewGroup) tableLayoutChild).getChildCount(); i++) {
                    View view = ((ViewGroup) tableLayoutChild).getChildAt(i);
                    if (view instanceof Button) {
                        buttonCount++;
                    }
                }
            }
        }
        return buttonCount;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.example.richardcurteis.connect3.MainActivity"
    tools:showIn="@layout/activity_main"
    android:background="#070000">

    <TableLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="false"
        android:layout_alignParentEnd="false"
        android:layout_alignParentStart="false"
        android:layout_centerInParent="true"
        android:id="@+id/tableLayout"
        android:background="#000000">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton1"
                android:layout_column="4"
                android:onClick="receiveClick"
                android:tag="0" />
                android:nestedScrollingEnabled="false" />

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton2"
                android:layout_column="12"
                android:onClick="receiveClick"
                android:tag="1" />

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton3"
                android:layout_column="19"
                android:onClick="receiveClick"
                android:tag="2" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton4"
                android:layout_column="4"
                android:onClick="receiveClick"
                android:tag="3"/>

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton5"
                android:layout_column="12"
                android:onClick="receiveClick"
                android:tag="4"/>

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton6"
                android:layout_column="19"
                android:onClick="receiveClick"
                android:tag="5"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"></TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton7"
                android:layout_column="4"
                android:onClick="receiveClick"
                android:tag="6"/>

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton8"
                android:layout_column="12"
                android:onClick="receiveClick"
                android:tag="7"/>

            <Button
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/gridButton9"
                android:layout_column="19"
                android:onClick="receiveClick"
                android:tag="8" />
        </TableRow>
    </TableLayout>

    <Button

        android:layout_width="200dp"
        android:layout_height="120dp"
        android:text="New Game"
        android:id="@+id/newGameButton"
        android:layout_below="@+id/tableLayout"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="61dp" />
</RelativeLayout>

Logs and errors:

01-04 23:08:58.520 4380-4380/com.example.richardcurteis.connect3 I/art: Not late-enabling -Xcheck:jni (already on)
01-04 23:08:58.627 4380-4380/com.example.richardcurteis.connect3 W/System: ClassLoader referenced unknown path: /data/app/com.example.richardcurteis.connect3-1/lib/x86
01-04 23:08:58.977 4380-4380/com.example.richardcurteis.connect3 I/System.out: Start: true
01-04 23:08:59.008 4380-4407/com.example.richardcurteis.connect3 D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
01-04 23:08:59.138 4380-4407/com.example.richardcurteis.connect3 I/OpenGLRenderer: Initialized EGL, version 1.4
01-04 23:08:59.192 4380-4407/com.example.richardcurteis.connect3 W/EGL_emulation: eglSurfaceAttrib not implemented
01-04 23:08:59.192 4380-4407/com.example.richardcurteis.connect3 W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xabe05620, error=EGL_SUCCESS
01-04 23:09:06.653 4380-4380/com.example.richardcurteis.connect3 I/System.out: [1, 2, 3, 4, 5, 6, 7, 8]
01-04 23:09:06.654 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: false
01-04 23:09:09.088 4380-4380/com.example.richardcurteis.connect3 I/System.out: [1, 2, 3, 4, 5, 6, 7, 8]
01-04 23:09:09.088 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:11.709 4380-4380/com.example.richardcurteis.connect3 I/System.out: [2, 3, 4, 5, 6, 7, 8]
01-04 23:09:11.709 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:15.156 4380-4380/com.example.richardcurteis.connect3 I/System.out: [3, 4, 5, 6, 7, 8]
01-04 23:09:15.156 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:16.418 4380-4380/com.example.richardcurteis.connect3 I/System.out: [3, 4, 6, 7, 8]
01-04 23:09:16.418 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:17.060 4380-4380/com.example.richardcurteis.connect3 I/System.out: [3, 6, 7, 8]
01-04 23:09:17.061 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:17.664 4380-4380/com.example.richardcurteis.connect3 I/System.out: [6, 7, 8]
01-04 23:09:17.672 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:18.378 4380-4380/com.example.richardcurteis.connect3 I/System.out: [7, 8]
01-04 23:09:18.378 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:18.905 4380-4380/com.example.richardcurteis.connect3 I/System.out: [8]
01-04 23:09:18.913 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:19.487 4380-4380/com.example.richardcurteis.connect3 I/System.out: []
01-04 23:09:19.489 4380-4380/com.example.richardcurteis.connect3 I/System.out: receiveClick: true
01-04 23:09:19.494 4380-4380/com.example.richardcurteis.connect3 D/AndroidRuntime: Shutting down VM
01-04 23:09:19.494 4380-4380/com.example.richardcurteis.connect3 E/AndroidRuntime: FATAL EXCEPTION: main
   Process: com.example.richardcurteis.connect3, PID: 4380
   java.lang.IllegalStateException: Could not execute method for android:onClick
       at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:275)
       at android.view.View.performClick(View.java:5198)
       at android.view.View$PerformClick.run(View.java:21147)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:148)
       at android.app.ActivityThread.main(ActivityThread.java:5417)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: java.lang.reflect.InvocationTargetException
       at java.lang.reflect.Method.invoke(Native Method)
       at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:270)
       at android.view.View.performClick(View.java:5198) 
       at android.view.View$PerformClick.run(View.java:21147) 
       at android.os.Handler.handleCallback(Handler.java:739) 
       at android.os.Handler.dispatchMessage(Handler.java:95) 
       at android.os.Looper.loop(Looper.java:148) 
       at android.app.ActivityThread.main(ActivityThread.java:5417) 
       at java.lang.reflect.Method.invoke(Native Method) 
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: java.lang.IllegalArgumentException: n <= 0: 0
       at java.util.Random.nextInt(Random.java:182)
       at com.example.richardcurteis.connect3.MainActivity.randomButtonPick(MainActivity.java:60)
       at com.example.richardcurteis.connect3.MainActivity.aiPlayerTurn(MainActivity.java:40)
       at com.example.richardcurteis.connect3.MainActivity.receiveClick(MainActivity.java:31)
       at java.lang.reflect.Method.invoke(Native Method) 
       at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:270) 
       at android.view.View.performClick(View.java:5198) 
       at android.view.View$PerformClick.run(View.java:21147) 
       at android.os.Handler.handleCallback(Handler.java:739) 
       at android.os.Handler.dispatchMessage(Handler.java:95) 
       at android.os.Looper.loop(Looper.java:148) 
       at android.app.ActivityThread.main(ActivityThread.java:5417) 
       at java.lang.reflect.Method.invoke(Native Method) 
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

Solution

  • True/False issue fixed. Problem was this line in onCreate():

    boolean noughtsTurn = true;

    The boolean, noughtsTurn was also declared at the top of the page as such.

    public class MainActivity extends AppCompatActivity {
    
        boolean noughtsTurn;
        ArrayList board;
        Players player;
        String aiPickedButton;
        ...
        ...
    

    This meant that it was always true. Changed to the below in onCreate and now the true/false switch works fine.

    noughtsTurn = true;