Search code examples
c#androidunity-game-enginebinaryfileschess

StockFish Chess Engine won't work on Android Device but, works on Standalone Build and Unity editor perfectly


I have been working on a chess game and using the Stockfish chess engine to implement AI in it. I was successful in firing up the executable file, sending fen code as input and receiving the output from the engine. It works perfectly on unity editor and standalone build. But, It won't work for the android device. I have no idea why.

I have made sure that the file is copied/created to the right directory and is successful. Can someone please help me figure out this issue?

    string fen;
    public static Process mProcess;

    void Start()
    {
        Setup();
    }


    public void Setup()
    {
        // since the apk file is archived this code retreives the stockfish binary data and
        // creates a copy of it in the persistantdatapath location.
#if UNITY_EDITOR

        string filepath = "D:\\Chess Projects\\StockFishTest\\Assets\\StreamingAssets\\stockfish_10_x64.exe";
#elif UNITY_ANDROID
        string filepath = Application.persistentDataPath + "/" + "stockfish-10-armv7";
        Debug.Log(filepath);
        if (!File.Exists(filepath))
        {
            WWW executable = new WWW("jar:file://" + Application.dataPath + "!/assets/" + "stockfish-10-armv7");
            while (!executable.isDone)
            {
            }
            File.WriteAllBytes(filepath, executable.bytes);

            //change permissions via plugin

        }
        var plugin = new AndroidJavaClass("com.chessbattles.jeyasurya.consoleplugin.AndroidConsole");
            string command = "chmod 777 "+filepath;
            outPut = plugin.CallStatic<string>("ExecuteCommand",command);

#else
        string filepath = Application.streamingAssetsPath+ "/" + "stockfish_10_x64.exe";
#endif
        // creating the process and communicating with the engine
        mProcess = new Process();
        ProcessStartInfo si = new ProcessStartInfo()
        {
            FileName = filepath,
            UseShellExecute = false,
            CreateNoWindow = true,
            RedirectStandardError = true,
            RedirectStandardInput = true,
            RedirectStandardOutput = true
        };
        mProcess.StartInfo = si;
        mProcess.OutputDataReceived += new DataReceivedEventHandler(MProcess_OutputDataReceived);
        mProcess.Start();
        mProcess.BeginErrorReadLine();
        mProcess.BeginOutputReadLine();

        SendLine("uci");
        SendLine("isready");

    }

    public void GetMove(string fen, int processTime = 0, int DepthValue = 1)
    {

        if(fen ==null || fen == ""){
            UnityEngine.Debug.LogError("Enter proper Fen");
            Debug.Log("Enter proper Fen");
            return;
        }

        SendLine("position fen "+ fen);

        if(processTime != 0){
            SendLine("go movetime "+processTime);
        }
        else if(DepthValue != 0)
        {
            SendLine("go depth "+ DepthValue);
        }
        else
        {
            SendLine("go depth " + DepthValue);
        }

    }

    public string output = "";
    public bool moveReady = false;
    public void SendLine(string command) {
        mProcess.StandardInput.WriteLine(command);
        mProcess.StandardInput.Flush();
    }

    void MProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        output = "";
    //    UnityEngine.Debug.Log("Output: " + e.Data);
        output = e.Data;
        if (output.Length != 0)
            if (output[0] == 'b' && output[3] == 't')
            {
                output = e.Data.Substring(9, 4);
      //          Debug.Log(output);
                moveReady = true;
            }
            else
            {
                moveReady = false;
            }

    }

Solution

  • I have managed to make this work like this. You need to update the package name in the file path.

    using System.Diagnostics;
    using System.IO;
    using UnityEngine;
    
    namespace Assets.Scripts
    {
        public class EngineCommunicator
        {
            public static Process mProcess;
            public static string fileName = "stockfish.android.armv7";
            public static void Communicate()
            {
    #if UNITY_EDITOR
                string filepath = "E:\\Personal\\Unity\\Chess2d\\Assets\\Plugins\\Windows\\stockfish_13_win_x64";
    #elif UNITY_ANDROID
                string filepath = "/data/data/com.tiringbring.Chess2d/lib/stockfish.android.armv7.so";
                UI.Instance.AddJob(() =>
                {
                    UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text = filepath;
                });
                
    
                if (!File.Exists(filepath))
                {
                    UI.Instance.AddJob(() =>
                    {
                        UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text =UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text +" "+ "file not found";
                    });
                }else{
                    UI.Instance.AddJob(() =>
                    {
                        UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text =UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text +" "+ "file found";
                    });
                }
    #else
            string filepath = Application.streamingAssetsPath+ "/" + "stockfish_13_x64.exe";
    #endif
                // creating the process and communicating with the engine
                mProcess = new Process();
                ProcessStartInfo si = new ProcessStartInfo()
                {
                    FileName = filepath,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    RedirectStandardError = true,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true
                };
                mProcess.StartInfo = si;
                mProcess.OutputDataReceived += new DataReceivedEventHandler(MProcess_OutputDataReceived);
                mProcess.Start();
                mProcess.BeginErrorReadLine();
                mProcess.BeginOutputReadLine();
    
                SendLine("uci");
                SendLine("isready");
                SendLine("ucinewgame");
                SendLine("position startpos");
                SendLine("go infinite searchmoves e2e4 d2d4");
    
    
            }
    
    
            private static void SendLine(string command)
            {
                mProcess.StandardInput.WriteLine(command);
                mProcess.StandardInput.Flush();
            }
    
            private static void MProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
            {
                string text = e.Data;
                UI.Instance.AddJob(() =>
                {
                    UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text = UI.Instance.TestText.GetComponent<TMPro.TextMeshProUGUI>().text +" "+ text;
                });
    
                UnityEngine.Debug.Log(text);
            }
        }
    }
    

    Put the file in Assets/Android directory with .so extension.

    enter image description here

    It will be copied automatically in lib folder on android mobile

    enter image description here