I am developing a Chess Game in Unity3D. I want to develop it for the Android platform. For AI I am using the Stockfish Chess Engine. I downloaded the Stockfish binary for Android named "Stockfish-9-armv7". I placed this binary file in my StreamingAssets folder so that it correctly goes into the targeted platform during build step. Everything works fine till here i.e. when I build my Unity project the file is placed in the correct location and I can very well see it.
Now in order for my AI to work I have to communicate with this binary file using UCI protocols. And so I wrote a C# script in my Unity project that creates a process to run the binary file and communicate with it. But this is not working.
However when I do the exact same thing for Windows i.e. using the windows binary version of Stockfish named "stockfish_9_x64.exe" and building it as a standalone application, things work perfectly and I am able to communicate with the engine via my C# code.
I researched it online but unable to find much resources and guidance. I found a similar post and reading through it led me conclude that maybe it has something to do with file permissions. The guy who asked this question actually solved the issue by writing these two lines of code:
string[] cmd = { "chmod", "744", Path.Combine(strToFolder, fileName) };
Java.Lang.Runtime.GetRuntime().Exec(cmd);
However he was using Xamarin and had access to Java Runtime library. I am using Unity and C# and I really don't know how to change the execute/run permission of this binary file and run it. In fact I don't even know whether this is the problem or not.
I just want to integrate stockfish into my Unity project with Android as the targeted platform. If anyone has any ideas, suggestions or if anyone has done this before, please guide me. Even if I am wrong from the start and my approach is buggy let me know that as well along with the corrected approach.
Given below is my code:
public class CommunicateWithEngine {
public static Process mProcess;
public static void Communicate()
{
// since the apk file is archived this code retreives the stockfish binary data and
// creates a copy of it in the persistantdatapath location.
string filepath = Application.persistentDataPath + "/" + "Stockfish-9-armv7";
if (!File.Exists(filepath))
{
WWW executable = new WWW("jar:file://" + Application.dataPath + "!/assets/" + "Stockfish-9-armv7");
while (!executable.isDone)
{
}
File.WriteAllBytes(filepath, executable.bytes);
}
// creating the process and communicating with the engine
mProcess = new Process();
ProcessStartInfo si = new ProcessStartInfo()
{
FileName = System.IO.Path.Combine(Application.persistentDataPath, "Stockfish-9-armv7"),
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");
}
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;
Test.PrintStringToTheConsole(text);
}
}
For anyone struggling with the same problem as OP and me. After 12 hours searching and googling here is a solution. You can use executable stockfish binary, but you have to change extension for binary to .so and import it in unity in folder /Assets/Plugins/Android. After build you can find it on device in folder /data/data//lib. Here you can execute it and get input and output.
Related
I want to get the path of an existing folder SeleniumTestData inside the solution.
Why? My selenium tests should create at start of the test, temporary folder which are being ignored in Git, so each of my colleagues has their own TestData folders for their own TestExecutions on their machine (Like Save/Load Cookies) and dont pull TestData from other colleagues.
The folder where i want to create other folder by code is named SeleniumTestData folder and is inside:
..\source\repos\CoffeeTalk\src\Tests
I cant hardcore the path, as i'm facing here 2 problems:
Tests are being ran in Windows and Docker (Linux)
Co-Workers are saving the solution in different windows directories
Now i need a general solution which will work in any of these cases.
I already tried: var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
which returned: D:\source\repos\CoffeeTalk\src\Tests\Web\CoffeeTalk.Client.Selenium.Tests\bin\Debug\net6.0
and then tried to navigate back by executing the codeline currentDirectory?.Parent about 5-6 times. But its then again different in Linux.
Now im looking for a clean way. I suppose the first way i did it was not wrong by getting the CurrentDirectory and navigate back.
I already searched for solutions using stackoverflow, google. Either the solutions are outdated or im not getting the result im expecting.
Here i have the method which creates the folder, but im struggling with the GetFolderPath method.
public static void CreateFolder(string folderName, string newFolderName)
{
var folderPath = GetFolderPath(folderName);
var pathCombined = Path.Combine(folderPath, newFolderName);
var folderExists = Directory.Exists(pathCombined);
if (folderExists) return;
Directory.CreateDirectory(pathCombined);
}
Directory.GetCurrentDirectory isn't the directory with your executable file. It's something else (I don't know) that by the way depends on the OS. You should use this instead:
using System.Reflection;
// ...
string exeDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
And then go up the folders hierarchy as you want:
string neededFolder = new DirectoryInfo(exeDirectory).Parent.Parent.Parent.ToString(); // Or more "Parent" calls
As far as I know, it works on different OSs.
did someone knows how to do that because i had investigate about, but i found only wrong/don't working answers I had try a lot of solutions but it seems to be wrong, like using the Chilkat directory , using ArchiveTransferManager ...
Chilkat.Rest rest = new Chilkat.Rest();
bool bTls = true;
int port = 443;
bool bAutoReconnect = true;
bool success = rest.Connect("glacier.eu-west-1.amazonaws.com", port, bTls, bAutoReconnect);
Chilkat.AuthAws authAws = new Chilkat.AuthAws();
authAws.AccessKey = ;
authAws.SecretKey = ;
authAws.ServiceName = "glacier";
authAws.Region = "us-west-1";
success = rest.SetAuthAws(authAws);
rest.AddHeader("x-amz-glacier-version", "2012-06-01");
string filePath = "20190422.csv";
Chilkat.Crypt2 crypt = new Chilkat.Crypt2();
crypt.HashAlgorithm = "sha256-tree-hash";
crypt.EncodingMode = "hexlower";
string treeHashHex = crypt.HashFileENC(filePath);
rest.AddHeader("x-amz-sha256-tree-hash", treeHashHex);
crypt.HashAlgorithm = "sha256";
string linearHashHex = crypt.HashFileENC(filePath);
authAws.PrecomputedSha256 = linearHashHex;
rest.AddHeader("x-amz-archive-description", filePath);
Chilkat.Stream fileStream = new Chilkat.Stream();
fileStream.SourceFile = filePath;
string responseStr = rest.FullRequestStream("POST", "/682988997959/vaults/streamqueuesvault", fileStream);
if (rest.LastMethodSuccess != true)
{
Debug.WriteLine(rest.LastErrorText);
return;
}
int respStatusCode = rest.ResponseStatusCode;
if (respStatusCode >= 400)
{
Debug.WriteLine("Response Status Code = " + Convert.ToString(respStatusCode));
Debug.WriteLine("Response Header:");
Debug.WriteLine(rest.ResponseHeader);
Debug.WriteLine("Response Body:");
Debug.WriteLine(responseStr);
return;
}
Debug.WriteLine("response status code = " + Convert.ToString(respStatusCode));
string archiveId = rest.ResponseHdrByName("x-amz-archive-id");
Debug.WriteLine("x-amz-archive-id = " + archiveId);
string location = rest.ResponseHdrByName("Location");
Debug.WriteLine("Location = " + location);
Here is a step by step guide on How to upload a file from my local machine to a vault of s3 glacier using c# in a console app?. First I would like to present some basic background information that will be used later in the solution. Feel free to skip ahead to the solution if you are smart on S3 Glacier.
If you have AWS SDK for .NET and VS already installed, you can download the Repo from Github.
Quick Intro to S3-Glacier
Amazon S3 Glacier is Amazons low cost long term storage service.
In Glacier terminology, an object is referred to as an Archive. Also the folders where you store archives are called Vaults. Its pretty simple - From the Glacier FAQ:
Q: How is data within Amazon S3 Glacier organized?
You store data in Amazon S3 Glacier as an archive. Each archive is assigned a unique archive ID that can later be used to retrieve the data. An archive can represent a single file or you may choose to combine several files to be uploaded as a single archive. You upload archives into vaults. Vaults are collections of archives that you use to organize your data.
When you upload objects to S3 Glacier, the objects don't immediately appear in your Glacier console. Your Glacier console will refresh once a day.
Amazon recommends you use the AWS SDK for .NET when developing C# applications that interface AWS services.
Simple Solution
Before you code, go into your AWS Console and create a S3 Glacier Vault name 'TestVault'.
At the time of this solution (April 2019), I suggest you use Visual Studio 2019. These steps are similar for earlier versions of Visual Studio.
The code I present was taken directly from the AWS SDK for .NET Documentation.
Once your visual studio is ready, then follow these steps:
Create a new project (use template -> Console App (.NET Framework) - not Console App (.NET Core) and name it ConsoleApp9
Add the AWS SDK to your project via NuGet package manager command.
Tools menu, select Nuget Package Manager, and click Package Manager Console.
then type Install-Package AWSSDK.
For a MAC use Project->Add Nuget Packages. Search for "AWSSDK.Glacier" and install it.
Below is the working code. You need to copy most of this into your Program.cs and remove the default "Hello World" code. Your final Program.cs code should look like
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Amazon.Glacier;
using Amazon.Glacier.Transfer;
using Amazon.Runtime;
namespace ConsoleApp9
{
class Program
{
static string vaultName = "TestVault";
static string archiveToUpload = "C:\\Windows\\Temp\\TEST-ARCHIVE.txt";
static void Main(string[] args)
{
try
{
var manager = new ArchiveTransferManager(Amazon.RegionEndpoint.USEast1);
// Upload an archive.
string archiveId = manager.Upload(vaultName, "upload archive test", archiveToUpload).ArchiveId;
Console.WriteLine("Archive ID: (Copy and save this ID for use in other examples.) : {0}", archiveId);
Console.WriteLine("To continue, press Enter");
Console.ReadKey();
}
catch (AmazonGlacierException e) { Console.WriteLine(e.Message); }
catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
catch (Exception e) { Console.WriteLine(e.Message); }
Console.WriteLine("To continue, press Enter");
Console.ReadKey();
}
}
}
Put the file that you want to be uploaded to Glacier as c:\Windows\Temp\Test-Archive.txt. You can put the file anywhere you want, just update the variable archiveToUpload in your code to reflect the location.
If your region is not USEast1, Change the AWS Region on the line just after the try:
var manager = new ArchiveTransferManager(Amazon.RegionEndpoint.YOUR-REGION);
Run the program and it will upload the file. If you have installed the AWS SDK before this will likely work just fine and you will have a screen that shows your archive id.:
If you run into permissions or authorization errors - please follow these steps on setting up authorization for the AWS SDK. I recommend using a Credentials File (2nd option from top). Other problems could be wrong Vault Name or it cant find the file on your machine.
When you go back to the Glacier console, you will not see any files uploaded. Glacier is low cost and slow moving compared to s3 and so your Vault contents are updated once a day.
As long as you get an ID in step 6, your file was successfully stored in Glacier.
Hope this helps and you find success.
Make sure your region is consistent. In the following code, "eu-west-1" is used in the Connect call, but "us-west-1" is used for authAws.Region.
bool success = rest.Connect("glacier.eu-west-1.amazonaws.com", port, bTls, bAutoReconnect);
Chilkat.AuthAws authAws = new Chilkat.AuthAws();
authAws.AccessKey = ;
authAws.SecretKey = ;
authAws.ServiceName = "glacier";
authAws.Region = "us-west-1";
I am creating a Chess Game in Unity. I want to build my game for Android. For AI I am using Stockfish Chess Engine and more particularly "Stockfish-9-arm64v8" file which is a binary file for Android. I have created a C# script which creates a process to run this binary file and communicate with it. When I try to Start my process the exception is raised ->
try{
mProcess.Start();
}
catch(Exception e){
Helper.PrintString(e.GetType().ToString()); // ----------------(1)
Helper.PrintString(e.Message); // --------------(2)
}
/*
(1) is printing :
system.componentmodel.win32exception
(2) is printing :
ApplicationName =
'/storage/emulated/0/Android/data/com.chessmania.chess/files/Stockfish-9- arm64v8', CommandLine = '', CurrentDirectory = ''
*/
Also my process info parameters are as follows:
ProcessStartInfo si = new ProcessStartInfo()
{
FileName = System.IO.Path.Combine(Application.persistentDataPath, "Stockfish-9-arm64v8"),
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
Can anyone please help me with it. I have been trying to resolve this issue past few days but unable to. Does it have something to do with file permission?? I mean shall I include some arguments in my ProcessInfoParameters to forcefully run the binary file?? I really don't know if this is the problem or something else? Correct me from the start if I am wrong.
I just want to integrate Stockfish Chess Engine with with Unity Project and build it for Android Platform. If anyone has any ideas or suggestions or if anyone has dealt with similar problem before, please let me know how to solve this issue. I would be grateful. Thanks for bearing with me till here :)
My basic problem was converting a .docx file to .pdf. The problem would be solved incase I was allowed to use Microsoft.Office.Interop.Word.dll, which i am not since the server will not have MS Office installed. So I needed a free/open-source library that would allow me to do so. And i came across docx4j.NET.
http://www.docx4java.org/blog/2014/09/docx-to-pdf-in-c-net/
This worked fine as long as I ran it as a Console App. The following is the concerned code snippet:
string fileIN = #"C:\Users\...\Visual Studio 2013\Projects\HRDapp\HRDapp\Letter_Templates\AP.docx";
string fileOUT = #"C:\Users\...\Visual Studio 2013\Projects\HRDapp\HRDapp\Letter_Templates\AP.pdf";
log.Info("Hello from Common Logging");
// Necessary, if slf4j-api and slf4j-NetCommonLogging are separate DLLs
ikvm.runtime.Startup.addBootClassPathAssembly(
System.Reflection.Assembly.GetAssembly(
typeof(org.slf4j.impl.StaticLoggerBinder)));
// Configure to find docx4j.properties
// .. add as URL the dir containing docx4j.properties (not the file itself!)
Plutext.PropertiesConfigurator.setDocx4jPropertiesDir(projectDir + #"src\samples\resources\");
java.io.File file = new java.io.File(fileIN);
// OK, do it..
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(file);
java.io.FileOutputStream fos = new java.io.FileOutputStream(new java.io.File(fileOUT));
org.docx4j.Docx4J.toPDF(wordMLPackage, fos);
fos.close();
In case of using this in a Web App, the code runs fine till
java.io.File file = new java.io.File(fileIN);
and gets stuck at
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(file);
Although the file path is correct and works fine in the console app, but there seems something else that I am missing here. The log also prints upto the following statement-
iisexpress.exe Information: 0 : [INFO] org.docx4j.jaxb.Context - Using Java 6/7 JAXB implementation
.. and stops. Any kind of reply directing me to the source of the error will be very helpful. Thanks.
As Jeroen (of IKVM fame) has explained, when there is no main assembly (eg in an ASP.NET application), the IKVM class loader can't find your assembly when the code is trying to dynamically load a class.
So you'll want to add not just:
ikvm.runtime.Startup.addBootClassPathAssembly(
System.Reflection.Assembly.GetAssembly(
typeof(org.slf4j.impl.StaticLoggerBinder)));
but also:
ikvm.runtime.Startup.addBootClassPathAssembly(
System.Reflection.Assembly.GetAssembly(
typeof(org.slf4j.LoggerFactory)));
ikvm.runtime.Startup.addBootClassPathAssembly(
System.Reflection.Assembly.GetAssembly(
typeof(org.docx4j.jaxb.Context)));
Back in .NET 1.0 days I wrote a method to return the target of a shortcut on MS Windows. It did this through using an interop to the Windows Script Hosting Object Model and brute forced through the COM interface:
private FileInfo GetFileFromShortcut(FileInfo shortcut)
{
FileInfo targetFile = null;
try
{
IWshRuntimeLibrary.WshShell wShell = new IWshRuntimeLibrary.WshShellClass();
IWshRuntimeLibrary.WshShortcut wShortcut = (IWshRuntimeLibrary.WshShortcut)wShell.CreateShortcut(shortcut.FullName);
// if the file wasn't a shortcut then the TargetPath comes back empty
string targetName = wShortcut.TargetPath;
if (targetName.Length > 0)
{
targetFile = new FileInfo(targetName);
}
}
catch (Exception)
{ // will return a null targetFile if anything goes wrong
}
return targetFile;
}
This still bugs me, and I was looking to replace this with something more elegant, but only if the replacement actually works at least as well. I still can't find a native C# way of finding the target of a shortcut. Is there one, or is this still the best way of doing this type of thing?
It looks like someone has written a class to manipulate shortcut files in C# called ShellLink, but it too uses COM.
Can't you just open the .lnk or .url file and parse it?
This talks about the same thing and shows what the files look like:
http://www.programmingtalk.com/showthread.php?t=7335
I got interested in this as well a while ago.
Here is the accepted response with a link to a (informal) description of the format of LNK files. Apparently, all available methods yet go through some API.