diff --git a/Analyzer/Analyzer.vcxproj b/Analyzer/Analyzer.vcxproj
index 217151a..5350ad0 100644
--- a/Analyzer/Analyzer.vcxproj
+++ b/Analyzer/Analyzer.vcxproj
@@ -14,21 +14,21 @@
{953104A5-7106-4468-9888-72D37CAE07CD}Win32ProjAnalyzer
- 10.0.17763.0
+ 10.0ApplicationtrueUnicode
- v141
+ v143ApplicationfalsetrueUnicode
- v141
+ v143
diff --git a/Controller/Controller.csproj b/Controller/Controller.csproj
index 2f760dd..724f663 100644
--- a/Controller/Controller.csproj
+++ b/Controller/Controller.csproj
@@ -1,5 +1,5 @@
-
+Debugx86
@@ -10,7 +10,7 @@
PropertiesControllerController
- v4.5
+ v4.8512
diff --git a/Controller/Properties/Resources.Designer.cs b/Controller/Properties/Resources.Designer.cs
index 0b5c1a4..4d5fcc7 100644
--- a/Controller/Properties/Resources.Designer.cs
+++ b/Controller/Properties/Resources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.18408
+// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -19,7 +19,7 @@ namespace Controller.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
diff --git a/Controller/Properties/Settings.Designer.cs b/Controller/Properties/Settings.Designer.cs
index 20c5764..6d8f528 100644
--- a/Controller/Properties/Settings.Designer.cs
+++ b/Controller/Properties/Settings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.18408
+// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -12,7 +12,7 @@ namespace Controller.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.6.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
diff --git a/Controller/app.config b/Controller/app.config
index 51278a4..3e0e37c 100644
--- a/Controller/app.config
+++ b/Controller/app.config
@@ -1,3 +1,3 @@
-
+
diff --git a/Malom3/App.config b/Malom3/App.config
index 95f68a6..06a53ef 100644
--- a/Malom3/App.config
+++ b/Malom3/App.config
@@ -1,9 +1,9 @@
-
+
-
+
diff --git a/Malom3/Malom3.vbproj b/Malom3/Malom3.vbproj
index f1bc824..ae80f80 100644
--- a/Malom3/Malom3.vbproj
+++ b/Malom3/Malom3.vbproj
@@ -1,5 +1,5 @@
-
+Debugx86
@@ -13,7 +13,7 @@
Malom512WindowsForms
- v4.5
+ v4.8publish\
@@ -140,6 +140,7 @@
TrueApplication.myapp
+ TrueTrue
diff --git a/Malom3/My Project/Application.Designer.vb b/Malom3/My Project/Application.Designer.vb
index e3ae1f4..e5c99f6 100644
--- a/Malom3/My Project/Application.Designer.vb
+++ b/Malom3/My Project/Application.Designer.vb
@@ -1,7 +1,7 @@
'------------------------------------------------------------------------------
'
' This code was generated by a tool.
-' Runtime Version:4.0.30319.18408
+' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
diff --git a/Malom3/My Project/Resources.Designer.vb b/Malom3/My Project/Resources.Designer.vb
index 27a1994..7096896 100644
--- a/Malom3/My Project/Resources.Designer.vb
+++ b/Malom3/My Project/Resources.Designer.vb
@@ -1,7 +1,7 @@
'------------------------------------------------------------------------------
'
' This code was generated by a tool.
-' Runtime Version:4.0.30319.18444
+' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
@@ -22,7 +22,7 @@ Namespace My.Resources
'''
''' A strongly-typed resource class, for looking up localized strings, etc.
'''
- _
diff --git a/Malom3/My Project/Settings.Designer.vb b/Malom3/My Project/Settings.Designer.vb
index a4a3428..19ce599 100644
--- a/Malom3/My Project/Settings.Designer.vb
+++ b/Malom3/My Project/Settings.Designer.vb
@@ -1,7 +1,7 @@
'------------------------------------------------------------------------------
'
' This code was generated by a tool.
-' Runtime Version:4.0.30319.18408
+' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
@@ -15,7 +15,7 @@ Option Explicit On
Namespace My
_
Partial Friend NotInheritable Class MySettings
Inherits Global.System.Configuration.ApplicationSettingsBase
@@ -29,7 +29,7 @@ Namespace My
Private Shared addedHandlerLockObject As New Object
_
- Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs)
+ Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs)
If My.Application.SaveMySettingsOnExit Then
My.Settings.Save()
End If
diff --git a/MalomAPI/MalomAPI.vbproj b/MalomAPI/MalomAPI.vbproj
index 8908837..7627b8c 100644
--- a/MalomAPI/MalomAPI.vbproj
+++ b/MalomAPI/MalomAPI.vbproj
@@ -10,8 +10,9 @@
MalomAPI512Windows
- v4.5
+ v4.8true
+ true
@@ -120,6 +121,7 @@
TrueApplication.myapp
+ TrueTrue
diff --git a/MalomAPI/My Project/Resources.Designer.vb b/MalomAPI/My Project/Resources.Designer.vb
index 29b113f..b85fc0a 100644
--- a/MalomAPI/My Project/Resources.Designer.vb
+++ b/MalomAPI/My Project/Resources.Designer.vb
@@ -22,7 +22,7 @@ Namespace My.Resources
'''
''' A strongly-typed resource class, for looking up localized strings, etc.
'''
- _
diff --git a/MalomAPI/My Project/Settings.Designer.vb b/MalomAPI/My Project/Settings.Designer.vb
index dc51f4a..9622d2d 100644
--- a/MalomAPI/My Project/Settings.Designer.vb
+++ b/MalomAPI/My Project/Settings.Designer.vb
@@ -13,42 +13,42 @@ Option Explicit On
Namespace My
-
- _
+
+ _
Partial Friend NotInheritable Class MySettings
Inherits Global.System.Configuration.ApplicationSettingsBase
-
- Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings), MySettings)
-
+
+ Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings)
+
#Region "My.Settings Auto-Save Functionality"
#If _MyType = "WindowsForms" Then
- Private Shared addedHandler As Boolean
+ Private Shared addedHandler As Boolean
- Private Shared addedHandlerLockObject As New Object
+ Private Shared addedHandlerLockObject As New Object
- _
- Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs)
- If My.Application.SaveMySettingsOnExit Then
- My.Settings.Save()
- End If
- End Sub
+ _
+ Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs)
+ If My.Application.SaveMySettingsOnExit Then
+ My.Settings.Save()
+ End If
+ End Sub
#End If
#End Region
-
+
Public Shared ReadOnly Property [Default]() As MySettings
Get
-
+
#If _MyType = "WindowsForms" Then
- If Not addedHandler Then
- SyncLock addedHandlerLockObject
- If Not addedHandler Then
- AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
- addedHandler = True
- End If
- End SyncLock
- End If
+ If Not addedHandler Then
+ SyncLock addedHandlerLockObject
+ If Not addedHandler Then
+ AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
+ addedHandler = True
+ End If
+ End SyncLock
+ End If
#End If
Return defaultInstance
End Get
diff --git a/MalomAPI_cpp/.clang-format b/MalomAPI_cpp/.clang-format
new file mode 100644
index 0000000..b797a99
--- /dev/null
+++ b/MalomAPI_cpp/.clang-format
@@ -0,0 +1,160 @@
+# SPDX-License-Identifier: GPL-3.0
+#
+# clang-format configuration file. Intended for clang-format >= 12.
+#
+# For more information, see:
+#
+# Documentation/process/clang-format.rst
+# https://clang.llvm.org/docs/ClangFormat.html
+# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+#
+---
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+#AlignConsecutiveBitFields: false
+AlignConsecutiveDeclarations: false
+AlignConsecutiveMacros: false
+AlignEscapedNewlines: DontAlign
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortLambdasOnASingleLine: All
+#AllowShortEnumsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: true
+BinPackParameters: true
+#BitFieldColonSpacing: Both
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: true
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: true
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: false
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: false
+ #BeforeLambdaBody: false
+ #BeforeWhile: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+#BreakBeforeConceptDeclarations: false
+BreakBeforeTernaryOperators: false
+BreakConstructorInitializers: BeforeComma
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerIndentWidth : 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DeriveLineEnding: true
+DerivePointerAlignment: false
+#EmptyLineBeforeAccessModifier: Never
+FixNamespaceComments: true
+ForEachMacros:
+ - forever
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"config\.h"'
+ Priority: -1
+ - Regex: '^<.*>'
+ Priority: 3
+ - Regex: '^.
+#
+#
+# Don't search for additional CPPLINT.cfg in parent directories.
+set noparent
+# Use 'ART_' as the cpp header guard prefix (e.g. #ifndef ART_PATH_TO_FILE_H_).
+root=..
+# Limit line length.
+linelength=1024
+# Ignore the following categories of errors, as specified by the filter:
+# (the filter settings are concatenated together)
+filter=-build/c++11
+filter=-build/header_guard
+filter=-build/include_subdir
+filter=-build/include_order
+filter=-build/include
+filter=-build/namespaces
+filter=-readability/casting
+filter=-readability/fn_size
+filter=-runtime/arrays
+filter=-runtime/int
+filter=-runtime/references
+filter=-runtime/string
+filter=-runtime/threadsafe_fn
+filter=-whitespace/comma
+filter=-whitespace/braces
+filter=-whitespace/comments
+filter=-whitespace/indent
+filter=-whitespace/parens
diff --git a/MalomAPI_cpp/MalomAPI_cpp.vcxproj b/MalomAPI_cpp/MalomAPI_cpp.vcxproj
new file mode 100644
index 0000000..f47f6fd
--- /dev/null
+++ b/MalomAPI_cpp/MalomAPI_cpp.vcxproj
@@ -0,0 +1,176 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {3d1bed67-7f9e-495c-b4ad-fd473286ec3f}
+ MalomAPIcpp
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level4
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level4
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ true
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MalomAPI_cpp/MalomAPI_cpp.vcxproj.filters b/MalomAPI_cpp/MalomAPI_cpp.vcxproj.filters
new file mode 100644
index 0000000..ffbd19e
--- /dev/null
+++ b/MalomAPI_cpp/MalomAPI_cpp.vcxproj.filters
@@ -0,0 +1,129 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/MalomAPI_cpp/perfect_api.cpp b/MalomAPI_cpp/perfect_api.cpp
new file mode 100644
index 0000000..ca89c28
--- /dev/null
+++ b/MalomAPI_cpp/perfect_api.cpp
@@ -0,0 +1,219 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "perfect_api.h"
+#include "perfect_player.h"
+#include "perfect_game_state.h"
+
+#include
+
+PerfectPlayer *MalomSolutionAccess::pp = nullptr;
+std::exception *MalomSolutionAccess::lastError = nullptr;
+
+int MalomSolutionAccess::getBestMove(int whiteBitboard, int blackBitboard,
+ int whiteStonesToPlace,
+ int blackStonesToPlace, int playerToMove,
+ bool onlyStoneTaking)
+{
+ initializeIfNeeded();
+
+ GameState s;
+
+ const int W = 0;
+ const int B = 1;
+
+ if ((whiteBitboard & blackBitboard) != 0) {
+ throw std::invalid_argument("whiteBitboard and blackBitboard shouldn't "
+ "have any overlap");
+ }
+
+ for (int i = 0; i < 24; i++) {
+ if ((whiteBitboard & (1 << i)) != 0) {
+ s.T[i] = W;
+ s.stoneCount[W] += 1;
+ }
+ if ((blackBitboard & (1 << i)) != 0) {
+ s.T[i] = B;
+ s.stoneCount[B] += 1;
+ }
+ }
+
+ s.phase = ((whiteStonesToPlace == 0 && blackStonesToPlace == 0) ? 2 : 1);
+ mustBeBetween("whiteStonesToPlace", whiteStonesToPlace, 0, Rules::maxKSZ);
+ mustBeBetween("blackStonesToPlace", blackStonesToPlace, 0, Rules::maxKSZ);
+ s.setStoneCount[W] = Rules::maxKSZ - whiteStonesToPlace;
+ s.setStoneCount[B] = Rules::maxKSZ - blackStonesToPlace;
+ s.kle = onlyStoneTaking;
+ mustBeBetween("playerToMove", playerToMove, 0, 1);
+ s.sideToMove = playerToMove;
+ s.moveCount = 10;
+
+ if (s.futureStoneCount(W) > Rules::maxKSZ) {
+ throw std::invalid_argument("Number of stones in whiteBitboard + "
+ "whiteStonesToPlace > " +
+ std::to_string(Rules::maxKSZ));
+ }
+ if (s.futureStoneCount(B) > Rules::maxKSZ) {
+ throw std::invalid_argument("Number of stones in blackBitboard + "
+ "blackStonesToPlace > " +
+ std::to_string(Rules::maxKSZ));
+ }
+
+ std::string errorMsg = s.setOverAndCheckValidSetup();
+ if (errorMsg != "") {
+ throw std::invalid_argument(errorMsg);
+ }
+ if (s.over) {
+ throw std::invalid_argument("Game is already over.");
+ }
+
+ s.lastIrrev = 0;
+
+ int ret = 0;
+
+ try {
+ ret = pp->chooseRandom(pp->goodMoves(s)).toBitBoard();
+ } catch (std::out_of_range &) {
+ throw std::runtime_error("We don't have a database entry for this "
+ "position. This can happen either if the "
+ "database is corrupted (missing files), or "
+ "sometimes when the position is not reachable "
+ "from the starting position.");
+ }
+
+ deinitializeIfNeeded();
+
+ return ret;
+}
+
+int MalomSolutionAccess::getBestMoveNoException(
+ int whiteBitboard, int blackBitboard, int whiteStonesToPlace,
+ int blackStonesToPlace, int playerToMove, bool onlyStoneTaking)
+{
+ try {
+ lastError = nullptr;
+ return getBestMove(whiteBitboard, blackBitboard, whiteStonesToPlace,
+ blackStonesToPlace, playerToMove, onlyStoneTaking);
+ } catch (std::exception &e) {
+ lastError = &e;
+ return 0;
+ }
+}
+
+std::string MalomSolutionAccess::getLastError()
+{
+ if (lastError == nullptr) {
+ return "No error";
+ }
+ return lastError->what();
+}
+
+void MalomSolutionAccess::initializeIfNeeded()
+{
+ if (pp != nullptr) {
+ return;
+ }
+
+ if (sec_val_path == "") {
+ sec_val_path = ".";
+ }
+
+ Rules::initRules();
+ setVariantStripped();
+ if (!Sectors::hasDatabase()) {
+ throw std::runtime_error("Database files not found in the current "
+ "working directory (" +
+ std::filesystem::current_path().string() +
+ ")");
+ }
+ pp = new PerfectPlayer();
+}
+
+void MalomSolutionAccess::deinitializeIfNeeded()
+{
+ Rules::cleanup();
+
+ if (pp == nullptr) {
+ return;
+ }
+
+ delete pp;
+
+ pp = nullptr;
+}
+
+void MalomSolutionAccess::mustBeBetween(std::string paramName, int value,
+ int min, int max)
+{
+ if (value < min || value > max) {
+ throw std::out_of_range(paramName + " must be between " +
+ std::to_string(min) + " and " +
+ std::to_string(max));
+ }
+}
+
+void MalomSolutionAccess::setVariantStripped()
+{
+ // copy-paste from Rules.cpp, but references to Main stripped
+
+ switch (Wrappers::Constants::variant) {
+ case (int)Wrappers::Constants::Variants::std:
+ std::memcpy(Rules::millPos, Rules::stdLaskerMillPos,
+ sizeof(Rules::stdLaskerMillPos));
+ std::memcpy(Rules::invMillPos, Rules::stdLaskerInvMillPos,
+ sizeof(Rules::stdLaskerInvMillPos));
+ std::memcpy(Rules::boardGraph, Rules::stdLaskerBoardGraph,
+ sizeof(Rules::stdLaskerBoardGraph));
+ std::memcpy(Rules::aLBoardGraph, Rules::stdLaskerALBoardGraph,
+ sizeof(Rules::stdLaskerALBoardGraph));
+ Rules::maxKSZ = 9;
+ Rules::variantName = "std";
+ break;
+ case (int)Wrappers::Constants::Variants::lask:
+ std::memcpy(Rules::millPos, Rules::stdLaskerMillPos,
+ sizeof(Rules::stdLaskerMillPos));
+ std::memcpy(Rules::invMillPos, Rules::stdLaskerInvMillPos,
+ sizeof(Rules::stdLaskerInvMillPos));
+ std::memcpy(Rules::boardGraph, Rules::stdLaskerBoardGraph,
+ sizeof(Rules::stdLaskerBoardGraph));
+ std::memcpy(Rules::aLBoardGraph, Rules::stdLaskerALBoardGraph,
+ sizeof(Rules::stdLaskerALBoardGraph));
+ Rules::maxKSZ = 10;
+ Rules::variantName = "lask";
+ break;
+ case (int)Wrappers::Constants::Variants::mora:
+ std::memcpy(Rules::millPos, Rules::moraMillPos,
+ sizeof(Rules::moraMillPos));
+ std::memcpy(Rules::invMillPos, Rules::moraInvMillPos,
+ sizeof(Rules::moraInvMillPos));
+ std::memcpy(Rules::boardGraph, Rules::moraBoardGraph,
+ sizeof(Rules::moraBoardGraph));
+ std::memcpy(Rules::aLBoardGraph, Rules::moraALBoardGraph,
+ sizeof(Rules::moraALBoardGraph));
+ Rules::maxKSZ = 12;
+ Rules::variantName = "mora";
+ break;
+ }
+
+ if (Wrappers::Constants::extended) {
+ Rules::maxKSZ = 12;
+ }
+}
diff --git a/MalomAPI_cpp/perfect_api.h b/MalomAPI_cpp/perfect_api.h
new file mode 100644
index 0000000..3f85ceb
--- /dev/null
+++ b/MalomAPI_cpp/perfect_api.h
@@ -0,0 +1,56 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#ifndef PERFECT_MALOM_SOLUTION_H_INCLUDED
+#define PERFECT_MALOM_SOLUTION_H_INCLUDED
+
+#include "perfect_player.h"
+
+class MalomSolutionAccess
+{
+private:
+ static PerfectPlayer *pp;
+ static std::exception *lastError;
+
+public:
+ static int getBestMove(int whiteBitboard, int blackBitboard,
+ int whiteStonesToPlace, int blackStonesToPlace,
+ int playerToMove, bool onlyStoneTaking);
+
+ static int getBestMoveNoException(int whiteBitboard, int blackBitboard,
+ int whiteStonesToPlace,
+ int blackStonesToPlace, int playerToMove,
+ bool onlyStoneTaking);
+
+ static std::string getLastError();
+
+ static int getBestMoveStr(std::string args);
+
+ static void initializeIfNeeded();
+ static void deinitializeIfNeeded();
+
+ static void mustBeBetween(std::string paramName, int value, int min,
+ int max);
+
+ static void setVariantStripped();
+};
+
+#endif // PERFECT_MALOM_SOLUTION_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_common.cpp b/MalomAPI_cpp/perfect_common.cpp
new file mode 100644
index 0000000..417f0dc
--- /dev/null
+++ b/MalomAPI_cpp/perfect_common.cpp
@@ -0,0 +1,35 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "perfect_common.h"
+
+#include
+
+std::string sec_val_path = ".";
+std::string sec_val_fname = "";
+FILE *f = nullptr;
+
+void failwith(std::string s)
+{
+ throw std::runtime_error(std::string(VARIANT_NAME) + ": " + s);
+}
diff --git a/MalomAPI_cpp/perfect_common.h b/MalomAPI_cpp/perfect_common.h
new file mode 100644
index 0000000..69d581d
--- /dev/null
+++ b/MalomAPI_cpp/perfect_common.h
@@ -0,0 +1,346 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef PERFECT_COMMON_H_INCLUDED
+#define PERFECT_COMMON_H_INCLUDED
+
+#include "perfect_platform.h"
+
+#include
+#include
+#include
+
+#define STANDARD 1
+#define MORABARABA 2
+#define LASKER 3
+
+//-------------------------------------
+// Settings:
+
+#define RULE_VARIANT STANDARD // STANDARD, MORABARABA, or LASKER
+
+#define FULL_BOARD_IS_DRAW 1 // 0 or 1
+
+//#define FULL_SECTOR_GRAPH //extended solution //comment or uncomment
+
+#define DD // distinguish draws (ultra) //comment or uncomment
+
+//#define STONE_DIFF //value of sectors is the stone difference (otherwise, get
+// values from the .secval file) //comment or uncomment
+
+//-------------------------------------
+
+#ifdef STONE_DIFF
+#ifndef DD
+static_assert(false, "");
+#endif
+#endif
+
+#ifdef DD
+#ifdef FULL_SECTOR_GRAPH
+static_assert(false, "sec_val range");
+#endif
+#endif
+
+#ifdef DD
+#ifndef STONE_DIFF
+const int eval_struct_size = 3; // byte
+#if RULE_VARIANT == STANDARD
+const int field2_offset = 12; // bit
+#else
+const int field2_offset = 14; // bit
+#endif
+#else
+const int eval_struct_size = 2; // byte
+const int field2_offset = 6; // bit
+#endif
+const int field1_size = field2_offset;
+const int field2_size = 8 * eval_struct_size - field2_offset;
+using field2_t = int16_t;
+#endif
+
+#ifdef STONE_DIFF
+const char stone_diff_flag = 1;
+#else
+const char stone_diff_flag = 0;
+#endif
+
+#ifdef DD
+#define FNAME_SUFFIX "2"
+#else
+#define FNAME_SUFFIX ""
+#endif
+
+using sec_val = int16_t;
+
+#ifdef DD
+const sec_val sec_val_min_value = -(1 << (field1_size - 1));
+#endif
+
+#ifndef DD
+// val, spec, sym, count (forditva)
+// a 256 ertekbol lefoglalunk 1-et a specnek, 1-et a val0-nak, 1-et a count0-nak
+// (ezert 253-...)
+#define MAX_VAL 178
+#define MAX_COUNT (253 - MAX_VAL - 15) /*60*/
+#define SPEC (MAX_VAL + 1)
+#endif
+
+const int version = 2;
+
+#if RULE_VARIANT == STANDARD
+#define VARIANT_NAME "std"
+#define GRAPH_FUNC_NOTNEG std_mora_graph_func
+#define MILL_POS_CNT 16
+#ifndef FULL_SECTOR_GRAPH
+const int max_ksz = 9;
+#endif
+#endif
+
+#if RULE_VARIANT == LASKER
+#define VARIANT_NAME "lask"
+#define GRAPH_FUNC_NOTNEG lask_graph_func
+#define MILL_POS_CNT 16
+#ifndef FULL_SECTOR_GRAPH
+const int max_ksz = 10;
+#endif
+#endif
+
+#if RULE_VARIANT == MORABARABA
+#define VARIANT_NAME "mora"
+#define GRAPH_FUNC_NOTNEG std_mora_graph_func
+#define MILL_POS_CNT 20
+#ifndef FULL_SECTOR_GRAPH
+const int max_ksz = 12;
+#endif
+#endif
+
+#ifdef FULL_SECTOR_GRAPH
+const int max_ksz = 12;
+//#pragma message ("Warning: max_ksz leveve")
+#endif
+
+extern std::string sec_val_path;
+extern std::string sec_val_fname;
+
+// This file is created by the solver
+// with the -writemovegenlookups switch.
+// The Controller automatically makes
+// this, if the file doesn't exist.
+const std::string movegen_file = (std::string) "C:\\malom_data_aux\\" +
+ VARIANT_NAME +
+ ".movegen";
+
+typedef int64_t board;
+
+// azert nem lehet int, mert van olyan
+// hasznalata, hogy pl. mask24<> i);
+ return r;
+ }
+};
+#endif
+
+#if DANNER
+#pragma message("Warning : Compiled in Danner mode! (conversion warnings)")
+#endif
+
+#if __cplusplus
+#define WRAPPER
+#endif
+
+//#define STATISTICS
+
+#ifdef STATISTICS
+#ifdef DD
+#pragma message("Warning : STATISTICS and DD are both defined!")
+#endif
+#endif
+
+// You must not store count in it
+struct val
+{
+ sec_val key1 {0};
+ int key2 {0};
+
+ val() { }
+ val(sec_val key1, int key2)
+ : key1(key1)
+ , key2(key2)
+ {
+ assert(key1); /* nem count */
+ }
+
+ bool operator==(const val &o) const
+ {
+ return key1 == o.key1 && key2 == o.key2;
+ }
+
+ // We correct towards directions depending on the sign of key2.
+ std::tuple tr() const
+ {
+ return std::make_tuple(static_cast(-abs(key1)), key2);
+ }
+
+ bool operator<(const val &o) const
+ {
+ return tr() < o.tr();
+ }
+ bool operator<=(const val &o) const { return tr() <= o.tr(); }
+ bool operator>(const val &o) const { return tr() > o.tr(); }
+ bool operator>=(const val &o) const { return tr() >= o.tr(); }
+
+ val undo_negate() { return val(-key1, key2 + 1); }
+};
+
+struct id
+{
+ int W {0};
+ int B {0};
+ int WF {0};
+ int BF {0};
+
+ id(int W, int B, int WF, int BF)
+ : W(W)
+ , B(B)
+ , WF(WF)
+ , BF(BF)
+ { }
+
+ id() { }
+
+ static id null() { return id {-1, -1, -1, -1}; }
+
+ void negate()
+ {
+ std::swap(W, B);
+ std::swap(WF, BF);
+ }
+
+ id operator-() const
+ {
+ id r = *this;
+ r.negate();
+ return r;
+ }
+
+ bool eks() const { return *this == -*this; }
+
+ bool transient() const
+ {
+#if RULE_VARIANT == STANDARD || RULE_VARIANT == MORABARABA
+ return !(WF == 0 && BF == 0);
+#else
+ return !(W != 0 && B != 0);
+#endif
+ }
+
+ bool twine() const
+ {
+ return !eks() && !transient();
+ }
+
+ std::string file_name()
+ {
+ char b[255];
+ SPRINTF(b, sizeof(b), "%s_%d_%d_%d_%d.sec%s", VARIANT_NAME, W, B, WF,
+ BF, FNAME_SUFFIX);
+ std::string r = std::string(b);
+ return r;
+ }
+
+ bool operator<(const id &o) const
+ {
+ return std::make_pair(std::make_pair(W, B), std::make_pair(WF, BF)) <
+ std::make_pair(std::make_pair(o.W, o.B), std::make_pair(o.WF, o.BF));
+ }
+ bool operator>(const id &o) const
+ {
+ return std::make_pair(std::make_pair(W, B), std::make_pair(WF, BF)) >
+ std::make_pair(std::make_pair(o.W, o.B), std::make_pair(o.WF, o.BF));
+ }
+
+ bool operator==(const id &o) const
+ {
+ return W == o.W && B == o.B && WF == o.WF && BF == o.BF;
+ }
+ bool operator!=(const id &o) const
+ {
+ return !(*this == o);
+ }
+
+ std::string to_string()
+ {
+ char buf[255];
+ SPRINTF(buf, sizeof(buf), "%s_%d_%d_%d_%d", VARIANT_NAME, W, B, WF, BF);
+ return std::string(buf);
+ }
+};
+
+template <>
+struct std::hash
+{
+ size_t operator()(const id &k) const
+ {
+ return static_cast(k.W) | (static_cast(k.B) << 4) |
+ (static_cast(k.WF) << 8) |
+ (static_cast(k.BF) << 12);
+ }
+};
+
+// this must be in the define path of WRAPPER
+#include "perfect_log.h"
+
+template
+std::string tostring(T x)
+{
+ std::stringstream ss;
+ ss << x;
+ return ss.str();
+}
+
+template
+int sign(T x)
+{
+ return x < 0 ? -1 : (x > 0 ? 1 : 0);
+}
+
+void failwith(std::string s);
+
+#endif // PERFECT_COMMON_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_debug.cpp b/MalomAPI_cpp/perfect_debug.cpp
new file mode 100644
index 0000000..851eeb5
--- /dev/null
+++ b/MalomAPI_cpp/perfect_debug.cpp
@@ -0,0 +1,99 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "perfect_debug.h"
+#include "perfect_common.h"
+
+#include
+#include
+#include
+
+const char *toclp(board b)
+{
+ board mask = 1;
+ std::vector kit(24, -1);
+ for (int i = 0; i < 24; i++) {
+ if ((mask << i) & b)
+ kit[i] = 0;
+ }
+ for (int i = 24; i < 48; i++) {
+ if ((mask << i) & b)
+ kit[i - 24] = 1;
+ }
+
+ std::stringstream ss;
+ for (int i = 0; i < 24; i++)
+ ss << kit[i] << ",";
+ ss << "0,0,0,2,9,9," << POPCNT((unsigned int)(b & mask24)) << ","
+ << POPCNT((unsigned int)((b & (mask24 << 24)) >> 24))
+ << ",False,60,-1000,0,3,malom2";
+
+ char *ret = new char[1024];
+ STRCPY(ret, 1024, ss.str().c_str());
+ return ret;
+}
+
+std::string toclp2(board b)
+{
+ board mask = 1;
+ std::vector kit(24, -1);
+ for (int i = 0; i < 24; i++) {
+ if ((mask << i) & b)
+ kit[i] = 0;
+ }
+ for (int i = 24; i < 48; i++) {
+ if ((mask << i) & b)
+ kit[i - 24] = 1;
+ }
+
+ std::stringstream ss;
+ for (int i = 0; i < 24; i++)
+ ss << kit[i] << ",";
+ ss << "0,0,0,2,9,9,3,3,False,60,-1000,0,3,malom2";
+
+ return ss.str();
+}
+
+std::string toclp3(board b, id id)
+{
+ board mask = 1;
+ std::vector kit(24, -1);
+ for (int i = 0; i < 24; i++) {
+ if ((mask << i) & b)
+ kit[i] = 0;
+ }
+ for (int i = 24; i < 48; i++) {
+ if ((mask << i) & b)
+ kit[i - 24] = 1;
+ }
+
+ std::stringstream ss;
+ for (int i = 0; i < 24; i++)
+ ss << kit[i] << ",";
+
+ ss << "0,0,0," << (id.WF ? 1 : 2) << "," << max_ksz - id.WF << ","
+ << max_ksz - id.BF << "," << id.W << "," << id.B
+ << ",False,60,-1000,0,3,malom2";
+
+ return ss.str();
+}
diff --git a/MalomAPI_cpp/perfect_debug.h b/MalomAPI_cpp/perfect_debug.h
new file mode 100644
index 0000000..fac69d4
--- /dev/null
+++ b/MalomAPI_cpp/perfect_debug.h
@@ -0,0 +1,37 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef PERFECT_DEBUG_H_INCLUDED
+#define PERFECT_DEBUG_H_INCLUDED
+
+#include "perfect_common.h"
+
+#include
+
+const char *toclp(board b);
+
+std::string toclp2(board b);
+
+std::string toclp3(board b, id id);
+
+#endif // PERFECT_DEBUG_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_eval_elem.cpp b/MalomAPI_cpp/perfect_eval_elem.cpp
new file mode 100644
index 0000000..b4a136c
--- /dev/null
+++ b/MalomAPI_cpp/perfect_eval_elem.cpp
@@ -0,0 +1,241 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "perfect_common.h"
+#include "perfect_eval_elem.h"
+
+#include
+
+eval_elem_sym::eval_elem_sym(cas c, int x)
+ : c(c)
+ , x(x)
+{ }
+bool eval_elem_sym::operator==(const eval_elem_sym &o) const
+{
+ return c == o.c && x == o.x;
+}
+eval_elem_sym::eval_elem_sym(const eval_elem &o)
+ : c((cas)o.c)
+ , x(o.x)
+{ }
+
+eval_elem::eval_elem(cas c, int x)
+ : c(c)
+ , x(x)
+{ }
+bool eval_elem::operator==(const eval_elem &o) const
+{
+ return c == o.c && x == o.x;
+}
+eval_elem::eval_elem(const eval_elem_sym &o)
+ : c((cas)o.c)
+ , x(o.x)
+{
+ assert(o.c != eval_elem_sym::sym);
+}
+
+eval_elem_sym::eval_elem_sym(const eval_elem_sym2 &o)
+{
+ if (o.cas() == eval_elem_sym2::Val) {
+ assert(abs(o.value().key1) == 1);
+ assert(o.value().key2 >= 0);
+ assert((o.value().key2 & 1) == (o.value().key1 < 0 ? 0 : 1));
+ c = val;
+ x = o.value().key2;
+ } else if (o.cas() == eval_elem_sym2::Count) {
+ c = count;
+ x = o.count();
+ } else { // sym
+ c = sym;
+ x = o.sym();
+ }
+}
+
+eval_elem::eval_elem(const eval_elem2 &o)
+{
+ if (o.cas() == eval_elem2::Val) {
+ assert(abs(o.value().key1) == 1);
+ assert(o.value().key2 >= 0);
+ assert((o.value().key2 & 1) == (o.value().key1 < 0 ? 0 : 1));
+ c = val;
+ x = o.value().key2;
+ } else { // count
+ c = count;
+ x = o.count();
+ }
+}
+
+eval_elem2::eval_elem2(val v)
+ : key1(v.key1)
+ , key2(v.key2)
+{ }
+eval_elem2::eval_elem2(int c)
+ : key1(0)
+ , key2(c)
+{ }
+
+val eval_elem2::value() const
+{
+ assert(cas() == Val);
+ return val {key1, key2};
+}
+
+int eval_elem2::count() const
+{
+ assert(cas() == Count);
+ return key2;
+}
+
+eval_elem2::Cas eval_elem2::cas() const
+{
+ return key1 ? Val : Count;
+}
+
+eval_elem2::eval_elem2(eval_elem ee)
+{
+ if (ee.c == eval_elem::val) {
+ key1 = ee.x & 1 ? 1 : -1;
+ key2 = ee.x;
+ } else {
+ key1 = 0;
+ key2 = ee.x;
+ }
+}
+
+bool eval_elem2::operator==(const eval_elem2 &o) const
+{
+ return *this <= o && *this >= o;
+}
+bool eval_elem2::operator!=(const eval_elem2 &o) const
+{
+ return !(*this == o);
+}
+
+eval_elem_sym2::eval_elem_sym2(val v)
+ : key1(v.key1)
+ , key2(v.key2)
+{ }
+eval_elem_sym2::eval_elem_sym2(int c)
+ : key1(0)
+ , key2(c)
+{ }
+eval_elem_sym2::eval_elem_sym2(sec_val key1, int key2)
+ : key1 {key1}
+ , key2 {key2}
+{ }
+
+eval_elem_sym2 eval_elem_sym2::make_sym(int s)
+{
+ return eval_elem_sym2(-s - 1);
+}
+
+val eval_elem_sym2::value() const
+{
+ assert(cas() == Val);
+ return val {key1, key2};
+}
+
+int eval_elem_sym2::count() const
+{
+ assert(cas() == Count);
+ return key2;
+}
+
+int eval_elem_sym2::sym() const
+{
+ assert(cas() == Sym);
+ return -(key2 + 1);
+}
+
+eval_elem_sym2::Cas eval_elem_sym2::cas() const
+{
+ return key1 ? Val : (key2 >= 0 ? Count : Sym);
+}
+
+eval_elem_sym2::eval_elem_sym2(eval_elem_sym ees)
+{
+ if (ees.c == eval_elem_sym::val) {
+ key1 = ees.x & 1 ? 1 : -1;
+ key2 = ees.x;
+ } else if (ees.c == eval_elem_sym::count) {
+ key1 = 0;
+ key2 = ees.x;
+ } else {
+ key1 = 0;
+ key2 = -ees.x - 1;
+ }
+}
+
+bool eval_elem_sym2::operator==(const eval_elem_sym2 &o) const
+{
+ return key1 == o.key1 && key2 == o.key2;
+}
+
+eval_elem2::eval_elem2(const eval_elem_sym2 &o)
+ : key1(o.key1)
+ , key2(o.key2)
+{
+ assert(o.cas() != eval_elem_sym2::Sym);
+}
+
+eval_elem_sym2::eval_elem_sym2(const eval_elem2 &o)
+ : key1(o.key1)
+ , key2(o.key2)
+{ }
+
+eval_elem2 eval_elem2::corr(int corr)
+{
+ sec_val new_key1 = static_cast(key1 + corr);
+
+ // magic, don't touch!
+ return eval_elem2 {new_key1,
+ sign((long long)new_key1 * key1) * key2};
+}
+
+bool eval_elem2::operator<(const eval_elem2 &b) const
+{
+ auto &a = *this;
+ if (a.key1 != b.key1)
+ return a.key1 < b.key1;
+ else if (a.key1 < 0)
+ return a.key2 < b.key2;
+ else if (a.key1 > 0)
+ return a.key2 > b.key2;
+ else
+ return false;
+}
+bool eval_elem2::operator>(const eval_elem2 &b) const
+{
+ auto &a = *this;
+ return b < a;
+}
+bool eval_elem2::operator<=(const eval_elem2 &b) const
+{
+ auto &a = *this;
+ return !(a > b);
+}
+bool eval_elem2::operator>=(const eval_elem2 &b) const
+{
+ auto &a = *this;
+ return !(a < b);
+}
diff --git a/MalomAPI_cpp/perfect_eval_elem.h b/MalomAPI_cpp/perfect_eval_elem.h
new file mode 100644
index 0000000..ba42166
--- /dev/null
+++ b/MalomAPI_cpp/perfect_eval_elem.h
@@ -0,0 +1,123 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef PERFECT_EVAL_ELEM_H_INCLUDED
+#define PERFECT_EVAL_ELEM_H_INCLUDED
+
+#include "perfect_common.h"
+
+struct eval_elem;
+struct eval_elem_sym2;
+struct eval_elem2;
+
+struct eval_elem_sym
+{
+ enum cas { val, count, sym };
+ cas c;
+ int x;
+ eval_elem_sym(cas c, int x);
+ bool operator==(const eval_elem_sym &o) const;
+ eval_elem_sym(const eval_elem &o);
+ eval_elem_sym(const eval_elem_sym2 &o);
+};
+
+struct eval_elem
+{
+ enum cas { val, count };
+ cas c;
+ int x;
+ eval_elem(cas c, int x);
+ bool operator==(const eval_elem &o) const;
+ eval_elem(const eval_elem_sym &o);
+ eval_elem(const eval_elem2 &o);
+};
+
+class Sector;
+
+struct eval_elem2
+{
+ // azert cannot be valid, because it cannot contain a count (as asserted by
+ // the ctor)
+ sec_val key1;
+ int key2;
+
+ enum Cas { Val, Count };
+
+ eval_elem2(val v);
+ eval_elem2(int c);
+ eval_elem2(eval_elem ee);
+ eval_elem2(sec_val key1, int key2)
+ : key1 {key1}
+ , key2 {key2}
+ { }
+
+ val value() const;
+ int count() const;
+ Cas cas() const;
+
+private:
+ bool operator==(const eval_elem2 &o) const;
+
+public:
+ bool operator!=(const eval_elem2 &o) const;
+ eval_elem2(const eval_elem_sym2 &o);
+ bool operator<(const eval_elem2 &b) const;
+ bool operator>(const eval_elem2 &b) const;
+ bool operator<=(const eval_elem2 &b) const;
+ bool operator>=(const eval_elem2 &b) const;
+
+ eval_elem2 corr(int corr);
+
+ // static eval_elem2 min_value();
+};
+
+struct eval_elem_sym2
+{
+ // azert cannot be valid, because it cannot contain a count (as asserted by
+ // the ctor)
+ sec_val key1;
+ int key2;
+
+ enum Cas { Val, Count, Sym };
+
+ eval_elem_sym2(val v);
+ eval_elem_sym2(int c);
+ static eval_elem_sym2 make_sym(int s);
+ eval_elem_sym2(eval_elem_sym ee);
+ eval_elem_sym2(sec_val key1, int key2);
+
+ val value() const;
+ int count() const;
+ int sym() const;
+ Cas cas() const;
+
+ bool operator==(const eval_elem_sym2 &o) const;
+ eval_elem_sym2(const eval_elem2 &o);
+
+#ifdef DD
+ static const field2_t spec_field2 = -(1 << (field2_size - 1));
+ static const field2_t max_field2 = -(spec_field2 + 1);
+#endif
+};
+
+#endif // PERFECT_EVAL_ELEM_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_game.cpp b/MalomAPI_cpp/perfect_game.cpp
new file mode 100644
index 0000000..9c63245
--- /dev/null
+++ b/MalomAPI_cpp/perfect_game.cpp
@@ -0,0 +1,110 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "perfect_player.h"
+#include "perfect_game.h"
+#include "perfect_game_state.h"
+
+class Player;
+class GameState;
+class CMove;
+
+GameState &Game::s() const
+{
+ // wrapper of current.value
+ return *current;
+}
+
+Game::Game(Player *p1, Player *p2)
+{
+ history.push_back(GameState());
+ current = std::prev(history.end());
+ _ply[0] = p1;
+ _ply[1] = p2;
+}
+
+Player **Game::plys()
+{
+ return _ply;
+}
+
+Player *Game::ply(int i) const
+{
+ // get players in the game
+ return _ply[i];
+}
+
+void Game::set_ply(int i, Player *p)
+{
+ // set players in the game
+ if (p == nullptr) {
+ _ply[i] = nullptr;
+ return;
+ }
+
+ // we exit p to see if it was in a game (e.g. NewGame in the
+ // previous one)
+ p->quit();
+
+ if (_ply[i] != nullptr)
+ _ply[i]->quit(); // the player replaced by p is kicked out
+ _ply[i] = p;
+ p->enter(this);
+}
+
+void Game::makeMove(CMove *M)
+{ // called by player objects when they want to move
+ try {
+ ply(1 - s().sideToMove)->followMove(M);
+
+ history.insert(std::next(current), GameState(s()));
+ current++;
+
+ s().makeMove(M);
+ } catch (std::exception &ex) {
+ // If TypeOf ex Is KeyNotFoundException Then Throw
+ std::cerr << "Exception in makeMove\n" << ex.what() << std::endl;
+ }
+}
+
+void Game::applySetup(GameState toSet)
+{
+ history.insert(std::next(current), toSet);
+ current++;
+}
+
+void Game::cancelThinking()
+{
+ for (int i = 0; i < 2; ++i) {
+ ply(i)->cancelThinking();
+ }
+}
+
+bool Game::playertypeChangingCmdAllowed()
+{
+ // Return TypeOf ply(s.sideToMove) Is HumanPlayer
+ return true;
+}
+
+void Game::copyMoveList()
+{
+ throw std::runtime_error("NotImplementedException");
+}
diff --git a/MalomAPI_cpp/perfect_game.h b/MalomAPI_cpp/perfect_game.h
new file mode 100644
index 0000000..5d7c67e
--- /dev/null
+++ b/MalomAPI_cpp/perfect_game.h
@@ -0,0 +1,64 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#ifndef PERFECT_GAME_H_INCLUDED
+#define PERFECT_GAME_H_INCLUDED
+
+#include "perfect_player.h"
+#include "perfect_rules.h"
+
+#include
+
+class Player;
+class CMove;
+
+class Game
+{
+private:
+ Player *_ply[2]; // players in the game
+ std::list history; // GameStates in this (and previous) games
+
+ // the node of the current GameState in history
+ std::list::iterator current;
+
+public:
+ GameState &s() const;
+
+ Game(Player *p1, Player *p2);
+
+ Player **plys();
+
+ Player *ply(int i) const;
+
+ void set_ply(int i, Player *p);
+
+ void makeMove(CMove *M);
+
+ void applySetup(GameState toSet);
+
+ void cancelThinking();
+
+ bool playertypeChangingCmdAllowed();
+
+ void copyMoveList();
+};
+
+#endif // PERFECT_MAIN_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_game_state.cpp b/MalomAPI_cpp/perfect_game_state.cpp
new file mode 100644
index 0000000..9f55d28
--- /dev/null
+++ b/MalomAPI_cpp/perfect_game_state.cpp
@@ -0,0 +1,339 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "perfect_api.h"
+#include "perfect_game_state.h"
+
+class Player;
+class GameState;
+class CMove;
+
+GameState::GameState(const GameState &s)
+{
+ T = s.T;
+ phase = s.phase;
+ setStoneCount = s.setStoneCount;
+ stoneCount = s.stoneCount;
+ kle = s.kle;
+ sideToMove = s.sideToMove;
+ moveCount = s.moveCount;
+ over = s.over;
+ winner = s.winner;
+ block = s.block;
+ lastIrrev = s.lastIrrev;
+}
+
+int GameState::futureStoneCount(int p)
+{
+ return stoneCount[p] + Rules::maxKSZ - setStoneCount[p];
+}
+
+// Sets the state for Setup Mode: the placed stones are unchanged, but we switch
+// to phase 2.
+void GameState::initSetup()
+{
+ moveCount = 10; // Nearly all the same, just don't be too small, see other
+ // comments
+ over = false;
+ // Winner can be undefined, as over = False
+ block = false;
+ lastIrrev = 0;
+}
+
+void GameState::makeMove(CMove *M)
+{
+ if (M == nullptr) {
+ throw std::invalid_argument("M is null");
+ }
+
+ checkInvariants();
+ checkValidMove(M);
+
+ moveCount++;
+
+ SetKorong *sk = dynamic_cast(M);
+ MoveKorong *mk = dynamic_cast(M);
+ LeveszKorong *lk = dynamic_cast(M);
+
+ if (sk != nullptr) {
+ T[sk->hov] = sideToMove;
+ setStoneCount[sideToMove]++;
+ stoneCount[sideToMove]++;
+ lastIrrev = 0;
+ } else if (mk != nullptr) {
+ T[mk->hon] = -1;
+ T[mk->hov] = sideToMove;
+ lastIrrev++;
+ if (lastIrrev >= Rules::lastIrrevLimit) {
+ over = true;
+ winner = -1; // draw
+ }
+ } else if (lk != nullptr) {
+ T[lk->hon] = -1;
+ stoneCount[1 - sideToMove]--;
+ kle = false;
+ if (stoneCount[1 - sideToMove] + Rules::maxKSZ -
+ setStoneCount[1 - sideToMove] <
+ 3) {
+ over = true;
+ winner = sideToMove;
+ }
+ lastIrrev = 0;
+ }
+
+ if ((sk != nullptr && Rules::malome(sk->hov, *this) > -1 &&
+ stoneCount[1 - sideToMove] > 0) ||
+ (mk != nullptr && Rules::malome(mk->hov, *this) > -1 &&
+ stoneCount[1 - sideToMove] > 0)) {
+ kle = true;
+ } else {
+ sideToMove = 1 - sideToMove;
+ if (setStoneCount[0] == Rules::maxKSZ &&
+ setStoneCount[1] == Rules::maxKSZ && phase == 1)
+ phase = 2;
+ if (!Rules::youCanMove(*this)) {
+ over = true;
+ block = true;
+ winner = 1 - sideToMove;
+ if (Wrappers::Constants::FBD && stoneCount[0] == 12 &&
+ stoneCount[1] == 12) {
+ winner = -1;
+ }
+ }
+ }
+
+ delete M;
+
+ checkInvariants();
+}
+
+void GameState::checkValidMove(CMove *M)
+{
+ // Hard to ensure that the 'over and winner = -1' case never occurs. For
+ // example, the WithTaking case of PerfectPlayer.MakeMoveInState is tricky,
+ // because the previous makeMove may have already made it a draw.
+ assert(!over || winner == -1);
+
+ SetKorong *sk = dynamic_cast(M);
+ MoveKorong *mk = dynamic_cast(M);
+ LeveszKorong *lk = dynamic_cast(M);
+
+ if (sk != nullptr) {
+ assert(phase == 1);
+ assert(T[sk->hov] == -1);
+ }
+ if (mk != nullptr) {
+ assert(T[mk->hon] == sideToMove);
+ assert(T[mk->hov] == -1);
+ }
+ if (lk != nullptr) {
+ assert(kle);
+ assert(T[lk->hon] == 1 - sideToMove);
+ }
+}
+
+void GameState::checkInvariants()
+{
+ assert(setStoneCount[0] >= 0);
+ assert(setStoneCount[0] <= Rules::maxKSZ);
+ assert(setStoneCount[1] >= 0);
+ assert(setStoneCount[1] <= Rules::maxKSZ);
+ assert(phase == 1 || (phase == 2 && setStoneCount[0] == Rules::maxKSZ &&
+ setStoneCount[1] == Rules::maxKSZ));
+}
+
+#if defined(_WIN32)
+#pragma warning(push)
+#pragma warning(disable : 4127)
+#endif
+// Called when applying a free setup. It sets over and checks whether the
+// position is valid. Returns "" if valid, reason str otherwise. Also called
+// when pasting a position.
+std::string GameState::setOverAndCheckValidSetup()
+{
+ assert(!over && !block);
+
+ // Validity checks:
+ // Note: this should be before setting over, because we will deny applying
+ // the setup if the state is not valid, and we want to maintain the 'Not
+ // over and Not block' invariants.
+
+ int toBePlaced0 = Rules::maxKSZ - setStoneCount[0];
+ if (stoneCount[0] + toBePlaced0 > Rules::maxKSZ) {
+ return "Too many white stones (on the board + to be placed). Please "
+ "remove some white stones from the board and/or decrease the "
+ "number of white stones to be placed.";
+ }
+ int toBePlaced1 = Rules::maxKSZ - setStoneCount[1];
+ if (stoneCount[1] + toBePlaced1 > Rules::maxKSZ) {
+ return "Too many black stones (on the board + to be placed). Please "
+ "remove some black stones from the board and/or decrease the "
+ "number of black stones to be placed.";
+ }
+
+ assert(!(phase == 1 && toBePlaced0 == 0 && toBePlaced1 == 0));
+ assert(!(phase == 2 && (toBePlaced0 > 0 || toBePlaced1 > 0)));
+
+ if (Wrappers::Constants::variant !=
+ (int)Wrappers::Constants::Variants::lask &&
+ !Wrappers::Constants::extended) {
+ if (phase == 1) {
+ if (toBePlaced0 !=
+ toBePlaced1 - (((sideToMove == 0) ^ kle) ? 0 : 1)) {
+ return "If Black is to move in the placement phase, then the "
+ "number of black stones to be placed should be one more "
+ "than the number of white stones to placed. If White is "
+ "to move in the placement phase, then the number of "
+ "white and black stones to be placed should be equal. "
+ "(Except in a stone taking position, where these "
+ "conditions are reversed.)\n\nNote: The Lasker variant "
+ "(and the extended solutions) doesn't have these "
+ "constraints.\n\nNote: You can switch the side to move "
+ "by the \"Switch STM\" button in position setup mode.";
+ }
+ } else {
+ if (phase != 2) {
+ throw std::runtime_error("Phase is not 2");
+ }
+ if (toBePlaced0 != 0 || toBePlaced1 != 0) {
+ throw std::runtime_error("toBePlaced0 or toBePlaced1 is not 0");
+ }
+ }
+ }
+
+ if (kle && stoneCount[1 - sideToMove] == 0) {
+ return "A position where the opponent doesn't have any stones cannot "
+ "be a stone taking position.";
+ }
+
+ // Set over if needed:
+ bool whiteLose = false, blackLose = false;
+ if (stoneCount[0] + Rules::maxKSZ - setStoneCount[0] < 3) {
+ whiteLose = true;
+ }
+ if (stoneCount[1] + Rules::maxKSZ - setStoneCount[1] < 3) {
+ blackLose = true;
+ }
+ if (whiteLose || blackLose) {
+ over = true;
+ if (whiteLose && blackLose) {
+ winner = -1; // draw
+ } else {
+ if (whiteLose) {
+ winner = 1;
+ } else {
+ assert(blackLose);
+ winner = 0;
+ }
+ }
+ }
+ if (!kle && !Rules::youCanMove(*this)) { // youCanMove doesn't handle the
+ // kle case. However, we should
+ // always have a move in kle, see
+ // the validity check above.
+ over = true;
+ block = true;
+ winner = 1 - sideToMove;
+ if (Wrappers::Constants::FBD && stoneCount[0] == 12 &&
+ stoneCount[1] == 12) {
+ winner = -1;
+ }
+ }
+
+ // Even though lastIrrev is always 0 while in free setup mode, it can be
+ // non-0 when pasting
+ if (lastIrrev >= Rules::lastIrrevLimit) {
+ over = true;
+ winner = -1;
+ }
+
+ return "";
+}
+#if defined(_WIN32)
+#pragma warning(pop)
+#endif
+
+// to paste from clipboard
+GameState::GameState(const std::string &s)
+{
+ std::vector ss;
+ std::string temp;
+ std::stringstream strStream(s);
+
+ // split by commas
+ while (std::getline(strStream, temp, ',')) {
+ ss.push_back(temp);
+ }
+
+ try {
+ if (ss[33] == "malom" || ss[34] == "malom" || ss[35] == "malom" ||
+ ss[37] == "malom2") { // you need to be able to interpret older
+ // formats as well
+ for (int i = 0; i < 24; i++) {
+ T[i] = std::stoi(ss[i]);
+ }
+ sideToMove = std::stoi(ss[24]);
+ phase = std::stoi(ss[27]);
+ setStoneCount[0] = std::stoi(ss[28]);
+ setStoneCount[1] = std::stoi(ss[29]);
+ stoneCount[0] = std::stoi(ss[30]);
+ stoneCount[1] = std::stoi(ss[31]);
+ kle = (ss[32] == "True" || ss[32] == "true");
+ moveCount = (ss[33] != "malom") ? std::stoi(ss[33]) : 10;
+ lastIrrev = ((ss[33] != "malom") && (ss[34] != "malom")) ?
+ std::stoi(ss[34]) :
+ 0;
+
+ // ensure correct count of stones
+ ptrdiff_t count0 = std::count(T.begin(), T.end(), 0);
+ ptrdiff_t count1 = std::count(T.begin(), T.end(), 1);
+ if (stoneCount[0] != count0 || stoneCount[1] != count1) {
+ throw InvalidGameStateException("Number of stones is "
+ "incorrect.");
+ }
+ } else {
+ throw std::invalid_argument("Invalid Format");
+ }
+ } catch (InvalidGameStateException &ex) {
+ throw ex;
+ } catch (std::exception &) {
+ throw std::invalid_argument("Invalid Format");
+ }
+}
+
+// for clipboard
+std::string GameState::toString()
+{
+ std::stringstream s;
+ for (int i = 0; i < 24; i++) {
+ s << T[i] << ",";
+ }
+ s << sideToMove << "," << 0 << "," << 0 << "," << phase << ","
+ << setStoneCount[0] << "," << setStoneCount[1] << "," << stoneCount[0]
+ << "," << stoneCount[1] << "," << (kle ? "True" : "False") << ","
+ << moveCount << "," << lastIrrev;
+ return s.str();
+}
+
+const char *InvalidGameStateException::what() const noexcept
+{
+ return mymsg.c_str();
+}
diff --git a/MalomAPI_cpp/perfect_game_state.h b/MalomAPI_cpp/perfect_game_state.h
new file mode 100644
index 0000000..b4f6b44
--- /dev/null
+++ b/MalomAPI_cpp/perfect_game_state.h
@@ -0,0 +1,85 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#ifndef PERFECT_GAME_STATE_H_INCLUDED
+#define PERFECT_GAME_STATE_H_INCLUDED
+
+#include
+
+class CMove; // forward declaration, implement this
+
+class GameState
+{
+public:
+ // The board (-1: empty, 0: white piece, 1: black piece)
+ std::vector T = std::vector(24, -1);
+ int phase = 1;
+ // How many stones the players have set
+ std::vector setStoneCount = std::vector(2, 0);
+ std::vector stoneCount = std::vector(2, 0);
+ bool kle = false; // Is there a puck removal coming?
+ int sideToMove = 0;
+ int moveCount = 0;
+ bool over = false;
+ int winner = 0; // (-1, if a draw)
+ bool block = false;
+ int lastIrrev = 0;
+
+ GameState() { } // start of game
+
+ GameState(const GameState &s);
+
+ int futureStoneCount(int p);
+
+ // Sets the state for Setup Mode: the placed stones are unchanged, but we
+ // switch to phase 2.
+ void initSetup();
+
+ void makeMove(CMove *M);
+
+ void checkValidMove(CMove *M);
+
+ void checkInvariants();
+
+ // Called when applying a free setup. It sets over and checks whether the
+ // position is valid. Returns "" if valid, reason str otherwise. Also called
+ // when pasting a position.
+ std::string setOverAndCheckValidSetup();
+
+ // to paste from clipboard
+ GameState(const std::string &s);
+
+ // for clipboard
+ std::string toString();
+};
+
+class InvalidGameStateException : public std::exception
+{
+public:
+ std::string mymsg;
+ InvalidGameStateException(const std::string &msg)
+ : mymsg(msg)
+ { }
+
+ virtual const char *what() const noexcept override;
+};
+
+#endif // PERFECT_GAME_STATE_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_hash.cpp b/MalomAPI_cpp/perfect_hash.cpp
new file mode 100644
index 0000000..e01a628
--- /dev/null
+++ b/MalomAPI_cpp/perfect_hash.cpp
@@ -0,0 +1,293 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "perfect_common.h"
+#include "perfect_hash.h"
+#include "perfect_symmetries.h"
+
+#include
+#include
+
+const int binom[25][25] = {
+ {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 4, 6, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 5, 10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 6, 15, 20, 15, 6, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 7, 21, 35, 35, 21, 7, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 8, 28, 56, 70, 56, 28, 8, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 9, 36, 84, 126, 126, 84, 36, 9, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91,
+ 14, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 15, 105, 455, 1365, 3003, 5005, 6435, 6435, 5005, 3003, 1365, 455,
+ 105, 15, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {1, 16, 120, 560, 1820, 4368, 8008, 11440, 12870,
+ 11440, 8008, 4368, 1820, 560, 120, 16, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0},
+ {1, 17, 136, 680, 2380, 6188, 12376, 19448, 24310,
+ 24310, 19448, 12376, 6188, 2380, 680, 136, 17, 1,
+ 0, 0, 0, 0, 0, 0, 0},
+ {1, 18, 153, 816, 3060, 8568, 18564, 31824, 43758,
+ 48620, 43758, 31824, 18564, 8568, 3060, 816, 153, 18,
+ 1, 0, 0, 0, 0, 0, 0},
+ {1, 19, 171, 969, 3876, 11628, 27132, 50388, 75582,
+ 92378, 92378, 75582, 50388, 27132, 11628, 3876, 969, 171,
+ 19, 1, 0, 0, 0, 0, 0},
+ {1, 20, 190, 1140, 4845, 15504, 38760, 77520, 125970,
+ 167960, 184756, 167960, 125970, 77520, 38760, 15504, 4845, 1140,
+ 190, 20, 1, 0, 0, 0, 0},
+ {1, 21, 210, 1330, 5985, 20349, 54264, 116280, 203490,
+ 293930, 352716, 352716, 293930, 203490, 116280, 54264, 20349, 5985,
+ 1330, 210, 21, 1, 0, 0, 0},
+ {1, 22, 231, 1540, 7315, 26334, 74613, 170544, 319770,
+ 497420, 646646, 705432, 646646, 497420, 319770, 170544, 74613, 26334,
+ 7315, 1540, 231, 22, 1, 0, 0},
+ {1, 23, 253, 1771, 8855, 33649, 100947, 245157, 490314,
+ 817190, 1144066, 1352078, 1352078, 1144066, 817190, 490314, 245157, 100947,
+ 33649, 8855, 1771, 253, 23, 1, 0},
+ {1, 24, 276, 2024, 10626, 42504, 134596,
+ 346104, 735471, 1307504, 1961256, 2496144, 2704156, 2496144,
+ 1961256, 1307504, 735471, 346104, 134596, 42504, 10626,
+ 2024, 276, 24, 1}};
+
+int next_choose(int x)
+{
+ if (x == 0)
+ return 1 << 24;
+
+ int c = x & -x, r = x + c;
+ return (((r ^ x) >> 2) / c) | r;
+}
+
+void init_collapse_lookup();
+
+Hash::Hash(int W, int B, Sector *s)
+ : W(W)
+ , B(B)
+ , s(s)
+{
+ g_lookup = new int[1LL << (24 - W)];
+
+ memset(f_lookup, -1, sizeof(f_lookup));
+ memset(f_sym_lookup2, 0, sizeof(f_sym_lookup2));
+ int c = 0;
+ for (int w = (1 << W) - 1; w < 1 << 24; w = next_choose(w))
+ if (f_lookup[w] == -1) {
+ for (int i = 0; i < 16; i++) {
+ // for(int i=15; i>=0; i--){
+ auto sw = sym24(i, w);
+ f_lookup[sw] = c;
+ f_sym_lookup[sw] = inv[i];
+ f_sym_lookup2[sw] |= 1 << inv[i];
+ }
+ /*
+ We call a state canonical that can be hashed (i.e., the inv_hash may
+ return it). A partition is one that has a matching hash. The
+ previous loop sometimes writes to the same place in f_sym_lookup
+ more than once. This corresponds to a table that can be symmetrized
+ into several canonical states (that is, they match on the white
+ part, but may vary on the black part) (several symmetry operations
+ (that overwrite the same) bring the whites into the same state, but
+ not necessarily the blacks). The point is that f_sym_lookup should
+ always lead to a canonical form. It usually doesn't matter which
+ one, except when we are already in a canonical form, because then it
+ is only allowed to lead into itself. (because partitions can only
+ look like they have one canonical member, and each points to the
+ canonical state) The line below is needed because if a symmetry
+ operation led the whites into itself, then the overwrite can lead
+ the canonical into another canonical with f_sym_lookup. The line
+ below corrects the f_sym value associated with the canonical. It
+ doesn't need to be corrected for the others, because they definitely
+ lead to a canonical (which is enough), since the inv[i] in the third
+ line of the loop body necessarily leads into w, which will be
+ canonical. (We call a position canonical if it is half of a proper
+ canonical (then it also fulfills that it is only half of
+ canonicals)) Thus, the first reached of a partition will certainly
+ be canonical, because thinking through a forward-backward hashing,
+ we get back such positions. (this is what the line below is for)
+
+ We call the sets obtained based on the above loop orbits. (we only
+ look at the white part of the positions) Below, the ws flip is
+ needed because the different elements of the set we get by
+ supplementing an orbit with blacks (everywhere in the same way (in a
+ collapsed sense)), may be in different partitions. The reason for
+ this is that the hash value at the g-s tag may differ because the
+ value of f_sym_lookup can be different at that tag.
+
+ New solution:
+ We replace the line below by putting the identical into the end of
+ the array of symmetry operations. Because then if the identical
+ collides with something, 0 will get into f_sym_lookup. // I mean,
+ this is written, right? So it's not "0 gets in", but identical gets
+ in.
+ */
+ // f_sym_lookup[w]=0;
+ c++;
+ }
+
+ f_count = c;
+ f_inv_lookup = new int[f_count];
+
+ std::vector ws;
+ for (int w = (1 << W) - 1; w < 1 << 24; w = next_choose(w))
+ ws.push_back(w);
+ reverse(ws.begin(), ws.end());
+ for (auto it = ws.begin(); it != ws.end(); ++it) {
+ auto w = *it;
+ f_inv_lookup[f_lookup[w]] = w;
+ }
+
+ g_inv_lookup = new int[binom[24 - W][B]];
+ c = 0;
+ for (int b = (1 << B) - 1; b < 1 << (24 - W); b = next_choose(b)) {
+ if (c >= binom[24 - W][B]) {
+ assert(false);
+ break;
+ }
+ g_lookup[b] = c;
+ g_inv_lookup[c] = b;
+ c++;
+ }
+
+ hash_count = f_count * binom[24 - W][B];
+
+ init_collapse_lookup();
+
+#ifdef _DEBUG
+#ifndef WRAPPER // The Wrapper uses the manual popcnt, which makes this
+ // noticeably slow when playing
+ check_hash_init_consistency();
+#endif
+#endif
+}
+
+void Hash::check_hash_init_consistency()
+{
+ for (int i = 0; i < 1 << 24; i++)
+ if (static_cast(POPCNT(i)) == W)
+ assert(f_sym_lookup[i] >= 0 && f_sym_lookup[i] < 16);
+}
+
+Hash::~Hash()
+{
+ delete [] f_inv_lookup;
+ delete [] g_lookup;
+ delete [] g_inv_lookup;
+}
+
+std::pair Hash::hash(board a)
+{
+ a = sym48(f_sym_lookup[a & mask24], a);
+ int h1 = f_lookup[a & mask24] * binom[24 - W][B] + g_lookup[collapse(a)];
+ eval_elem_sym2 e = s->get_eval_inner(h1);
+ if (e.cas() != eval_elem_sym2::Sym)
+ return std::make_pair(h1, e);
+ else {
+ a = sym48(e.sym(), a);
+ int h2 = f_lookup[a & mask24] * binom[24 - W][B] +
+ g_lookup[collapse(a)];
+ assert(s->get_eval_inner(h2).cas() != eval_elem_sym2::Sym);
+ return std::make_pair(h2, s->get_eval(h2));
+ }
+}
+
+board Hash::inv_hash(int h)
+{
+ int m = binom[24 - W][B];
+ int f = h / m, g = h % m;
+ return uncollapse(f_inv_lookup[f] | ((board)g_inv_lookup[g] << 24));
+}
+
+board uncollapse(board a)
+{
+ int w = (int)(a & mask24), b = (int)(a >> 24), r = 0;
+ for (int i = 1; i < 1 << 24; i <<= 1)
+ if (w & i)
+ b <<= 1;
+ else
+ r |= b & i;
+ return ((board)r << 24) | w;
+}
+
+// Original version
+// ~83 clock cycles if we increment the hash one by one (probably the branch
+// prediction is good at this time, since the positions are similar to each
+// other)
+// __declspec(noinline)
+#ifdef WRAPPER
+int collapse(board a)
+{
+ int w = (int)(a & mask24), b = (int)(a >> 24);
+ int i = 1, j = 1, r = 0;
+ for (; i < 1 << 24; i <<= 1) {
+ if (!(w & i)) {
+ r |= b & j;
+ j <<= 1;
+ } else
+ b >>= 1;
+ }
+ return r;
+}
+#endif
+
+// 8: 1:24
+// 6: 1:29
+// 4: 1:32
+const int sl = 8, psl = 1 << sl;
+unsigned char collapse_lookup[psl][psl];
+
+void init_collapse_lookup()
+{
+ // LOG("init_collapse_lookup");
+
+ for (int w = 0; w < psl; w++)
+ for (int bl = 0; bl < psl; bl++) {
+ int b = bl;
+ int i = 1, j = 1, r = 0;
+ for (; i < psl; i <<= 1) {
+ if (!(w & i)) {
+ r |= b & j;
+ j <<= 1;
+ } else
+ b >>= 1;
+ }
+ collapse_lookup[w][bl] = static_cast(r);
+ }
+
+ // LOG(".\n");
+}
diff --git a/MalomAPI_cpp/perfect_hash.h b/MalomAPI_cpp/perfect_hash.h
new file mode 100644
index 0000000..9673212
--- /dev/null
+++ b/MalomAPI_cpp/perfect_hash.h
@@ -0,0 +1,67 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef PERFECT_HASH_H_INCLUDED
+#define PERFECT_HASH_H_INCLUDED
+
+#include "perfect_sector.h"
+
+#include
+
+// void init_hash_lookuptables();
+
+class Hash
+{
+ int W, B; // It might be worth to put these after the large arrays for cache
+ // locality reasons
+
+ int f_lookup[1 << 24] {0};
+ char f_sym_lookup[1 << 24] {0}; // Converted from int to char
+ int *f_inv_lookup {nullptr};
+ int *g_lookup {nullptr};
+ int *g_inv_lookup {nullptr};
+
+ int f_count {0};
+
+ Sector *s {nullptr};
+
+public:
+ Hash(int W, int B, Sector *s);
+
+ std::pair hash(board a);
+ board inv_hash(int h);
+
+ int hash_count {0};
+
+ unsigned short f_sym_lookup2[1 << 24];
+
+ void check_hash_init_consistency();
+
+ ~Hash();
+};
+
+int collapse(board a);
+board uncollapse(board a);
+int next_choose(int x);
+
+#endif // PERFECT_HASH_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_log.cpp b/MalomAPI_cpp/perfect_log.cpp
new file mode 100644
index 0000000..b6c5cb3
--- /dev/null
+++ b/MalomAPI_cpp/perfect_log.cpp
@@ -0,0 +1,67 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "perfect_common.h"
+#include "perfect_log.h"
+
+#include
+
+bool Log::log_to_file = false;
+FILE *Log::logfile = stdout;
+std::string Log::fname, Log::fnamelogging, Log::donefname;
+
+void Log::setup_logfile(std::string filename, std::string extension)
+{
+ Log::fname = filename;
+ log_to_file = true;
+ fnamelogging = filename + ".logging" + FNAME_SUFFIX;
+
+ donefname = filename + "." + extension + FNAME_SUFFIX;
+
+ remove(donefname.c_str());
+ if (FOPEN(&logfile, fnamelogging.c_str(), "w") == -1) {
+ std::string errMsg = "Fatal error: Unable to open log file. (Another "
+ "instance is "
+ "probably running with the same parameters.)";
+ std::cerr << errMsg << std::endl;
+#if defined(_WIN32) || defined(_WIN64)
+ system("pause");
+#endif
+ throw std::runtime_error(errMsg);
+ }
+ if (logfile == nullptr) {
+ throw std::runtime_error("Failed to set buffer for the log file "
+ "because it is null.");
+ }
+ setvbuf(logfile, 0, _IONBF, 0);
+}
+
+void Log::close()
+{
+ if (log_to_file) {
+ fclose(logfile);
+ if (rename(fnamelogging.c_str(), donefname.c_str()))
+ std::cout << "Renaming logfile failed; errno: " << errno
+ << std::endl;
+ }
+}
diff --git a/MalomAPI_cpp/perfect_log.h b/MalomAPI_cpp/perfect_log.h
new file mode 100644
index 0000000..78cab6a
--- /dev/null
+++ b/MalomAPI_cpp/perfect_log.h
@@ -0,0 +1,65 @@
+/*
+Malom, a Nine Men's Morris (and variants) player and solver program.
+Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+
+See our webpage (and the paper linked from there):
+http://compalg.inf.elte.hu/~ggevay/mills/index.php
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef PERFECT_LOG_H_INCLUDED
+#define PERFECT_LOG_H_INCLUDED
+
+#include
+#include
+#include
+
+struct Log
+{
+ // This is not in the other branch because log.cpp is not included in the
+ // wrapper project (but there would be no obstacle to adding it)
+ static bool log_to_file;
+ static FILE *logfile;
+ static void setup_logfile(std::string fname, std::string extension);
+ static std::string fname, fnamelogging, donefname;
+ static void close();
+};
+
+template
+void LOG(const char *format, Args... args)
+{
+#if defined(_WIN32)
+ printf_s(format, args...);
+ fflush(stdout);
+ if (Log::log_to_file) {
+ fprintf_s(Log::logfile, format, args...);
+ fflush(Log::logfile);
+ }
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-security"
+ printf(format, args...);
+ fflush(stdout);
+ if (Log::log_to_file) {
+ fprintf(Log::logfile, format, args...);
+ fflush(Log::logfile);
+ }
+#pragma GCC diagnostic pop
+#endif
+}
+
+#endif // PERFECT_LOG_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_move.cpp b/MalomAPI_cpp/perfect_move.cpp
new file mode 100644
index 0000000..7edb527
--- /dev/null
+++ b/MalomAPI_cpp/perfect_move.cpp
@@ -0,0 +1,54 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "perfect_move.h"
+#include "perfect_api.h"
+#include "perfect_player.h"
+#include "perfect_rules.h"
+
+std::vector SetKorong::getMezok()
+{
+ return {hov};
+}
+
+std::string SetKorong::toString()
+{
+ return mezoToString[hov];
+}
+
+std::vector MoveKorong::getMezok()
+{
+ return {hon, hov};
+}
+
+std::string MoveKorong::toString()
+{
+ return mezoToString[hon] + "-" + mezoToString[hov];
+}
+
+std::vector LeveszKorong::getMezok()
+{
+ return {hon};
+}
+std::string LeveszKorong::toString()
+{
+ return "x" + mezoToString[hon];
+}
diff --git a/MalomAPI_cpp/perfect_move.h b/MalomAPI_cpp/perfect_move.h
new file mode 100644
index 0000000..7740c2e
--- /dev/null
+++ b/MalomAPI_cpp/perfect_move.h
@@ -0,0 +1,75 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#ifndef PERFECT_MOVE_H_INCLUDED
+#define PERFECT_MOVE_H_INCLUDED
+
+#include "perfect_player.h"
+#include "perfect_rules.h"
+
+class CMove
+{
+public:
+ virtual std::vector getMezok() = 0; // Returns the fields included in
+ // the step
+ virtual ~CMove() = default;
+protected:
+ std::string mezoToString[24] = {"a4", "a7", "d7", "g7", "g4", "g1",
+ "d1", "a1", "b4", "b6", "d6", "f6",
+ "f4", "f2", "d2", "b2", "c4", "c5",
+ "d5", "e5", "e4", "e3", "d3", "c3"};
+};
+
+class SetKorong : public CMove
+{
+public:
+ int hov;
+ SetKorong(int m)
+ : hov(m)
+ { }
+ std::vector getMezok() override;
+ std::string toString();
+};
+
+class MoveKorong : public CMove
+{
+public:
+ int hon, hov; // from, to
+ MoveKorong(int m1, int m2)
+ : hon(m1)
+ , hov(m2)
+ { }
+ std::vector getMezok() override;
+ std::string toString();
+};
+
+class LeveszKorong : public CMove
+{
+public:
+ int hon;
+ LeveszKorong(int m)
+ : hon(m)
+ { }
+ std::vector getMezok() override;
+ std::string toString();
+};
+
+#endif // PERFECT_MOVE_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_platform.h b/MalomAPI_cpp/perfect_platform.h
new file mode 100644
index 0000000..02221eb
--- /dev/null
+++ b/MalomAPI_cpp/perfect_platform.h
@@ -0,0 +1,47 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#ifndef PERFECT_PLATFORM_H_INCLUDED
+#define PERFECT_PLATFORM_H_INCLUDED
+
+#include
+#include
+
+#if defined(_WIN32)
+#define SPRINTF(buffer, buffer_size, format, ...) \
+ sprintf_s(buffer, buffer_size, format, ##__VA_ARGS__)
+#define FOPEN(file, filename, mode) fopen_s(file, filename, mode)
+#define FSCANF(file, format, ...) fscanf_s(file, format, ##__VA_ARGS__)
+#define STRCPY(destination, destination_size, source) \
+ strcpy_s(destination, destination_size, source)
+#define POPCNT(x) __popcnt(x)
+#else // _WIN32
+#define SPRINTF(buffer, buffer_size, format, ...) \
+ snprintf(buffer, buffer_size, format, ##__VA_ARGS__)
+#define FOPEN(file, filename, mode) \
+ ((*file = fopen(filename, mode)) != NULL ? 0 : -1)
+#define FSCANF(file, format, ...) fscanf(file, format, ##__VA_ARGS__)
+#define STRCPY(destination, destination_size, source) \
+ strncpy(destination, source, destination_size)
+#define POPCNT(x) __builtin_popcount(x)
+#endif // _WIN32
+
+#endif // PERFECT_PLATFORM_H_INCLUDED
diff --git a/MalomAPI_cpp/perfect_player.cpp b/MalomAPI_cpp/perfect_player.cpp
new file mode 100644
index 0000000..0300dd7
--- /dev/null
+++ b/MalomAPI_cpp/perfect_player.cpp
@@ -0,0 +1,489 @@
+// Malom, a Nine Men's Morris (and variants) player and solver program.
+// Copyright(C) 2007-2016 Gabor E. Gevay, Gabor Danner
+// Copyright (C) 2023 The Sanmill developers (see AUTHORS file)
+//
+// See our webpage (and the paper linked from there):
+// http://compalg.inf.elte.hu/~ggevay/mills/index.php
+//
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "perfect_api.h"
+#include "perfect_player.h"
+#include "perfect_game_state.h"
+#include "perfect_move.h"
+#include "perfect_rules.h"
+
+#include "perfect_wrappers.h"
+
+#include
+#include // for assert
+#include // for int64_t
+#include // for std::exit
+#include // for std::exception
+#include
+#include
+#include // for std::cerr
+#include ApplicationtrueUnicode
- v141
+ v143ApplicationtrueUnicode
- v141
+ v143ApplicationfalsetrueUnicode
- v141
+ v143
diff --git a/Wrappers/Hash.vcxproj b/Wrappers/Hash.vcxproj
index bc0d627..ab854c0 100644
--- a/Wrappers/Hash.vcxproj
+++ b/Wrappers/Hash.vcxproj
@@ -12,11 +12,11 @@
{D8FEE320-54D4-4779-A9C0-F97521ACE759}
- v4.5
+ v4.7.2ManagedCProjWrappersWrappers
- 10.0.17763.0
+ 10.0
@@ -24,14 +24,14 @@
truetrueUnicode
- v141
+ v143DynamicLibraryfalsetrueUnicode
- v141
+ v143