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;
}
}
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.
It will be copied automatically in lib folder on android mobile