commit 04154c0f83df35ed174b75faf10fbc6183a6a585
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Apr 3 01:41:03 2023 +0200

    Skeleton of a Pyraminx Diamond solver.

diff --git a/src/main/java/org/distorted/solvers/ImplementedSolversList.java b/src/main/java/org/distorted/solvers/ImplementedSolversList.java
index 28975b0f..c577327c 100644
--- a/src/main/java/org/distorted/solvers/ImplementedSolversList.java
+++ b/src/main/java/org/distorted/solvers/ImplementedSolversList.java
@@ -26,8 +26,8 @@ public enum ImplementedSolversList
   DIAMOND        (ObjectSignatures.DIAM_2, R.string.solver_diam2_title, R.string.solver_diam2_description, true),
   JING2          (ObjectSignatures.JING_2, R.string.solver_jing2_title, R.string.solver_jing2_description, true),
   DINO6          (ObjectSignatures.DINO_3, R.string.solver_dino6_title, R.string.solver_dino6_description, true),
-  DINO4          (ObjectSignatures.DIN4_3, R.string.solver_dino4_title, R.string.solver_dino4_description, false),
-  PDIA           (ObjectSignatures.PDIA_3, R.string.solver_pdia_title, R.string.solver_pdia_description, false),
+  DINO4          (ObjectSignatures.DIN4_3, R.string.solver_dino4_title, R.string.solver_dino4_description, true),
+  PDIA           (ObjectSignatures.PDIA_3, R.string.solver_pdia_title, R.string.solver_pdia_description, true),
   ;
 
   public static final int NUM_OBJECTS = values().length;
diff --git a/src/main/java/org/distorted/solvers/SolverMain.java b/src/main/java/org/distorted/solvers/SolverMain.java
index c8d78ac2..92dba019 100644
--- a/src/main/java/org/distorted/solvers/SolverMain.java
+++ b/src/main/java/org/distorted/solvers/SolverMain.java
@@ -126,6 +126,11 @@ public class SolverMain implements Runnable
       SolverTablebase solver = new SolverDino4(mRes,mObject);
       solver.solve(screen);
       }
+    else if( mSignature==ObjectSignatures.PDIA_3 )
+      {
+      SolverTablebase solver = new SolverPyraminxDiamond(mRes,mObject);
+      solver.solve(screen);
+      }
     else
       {
       screen.displayErrorDialog(mRes.getString(R.string.solver_generic_not_implemented));
diff --git a/src/main/java/org/distorted/solvers/SolverPyraminxDiamond.java b/src/main/java/org/distorted/solvers/SolverPyraminxDiamond.java
new file mode 100644
index 00000000..eadda499
--- /dev/null
+++ b/src/main/java/org/distorted/solvers/SolverPyraminxDiamond.java
@@ -0,0 +1,379 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2023 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Magic Cube.                                                              //
+//                                                                                               //
+// Magic Cube is proprietary software licensed under an EULA which you should have received      //
+// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.solvers;
+
+import android.content.res.Resources;
+
+import org.distorted.main.R;
+import org.distorted.objectlib.main.ObjectSignatures;
+import org.distorted.objectlib.main.TwistyObject;
+import org.distorted.objectlib.tablebases.ImplementedTablebasesList;
+import org.distorted.objectlib.tablebases.TablebaseHelpers;
+import org.distorted.objectlib.tablebases.TablebasesAbstract;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class SolverPyraminxDiamond extends SolverTablebase
+{
+  private static final int ERROR_CORNER_FR_MISSING = -1;
+  private static final int ERROR_CORNER_BR_MISSING = -2;
+  private static final int ERROR_CORNER_BL_MISSING = -3;
+  private static final int ERROR_CORNER_FL_MISSING = -4;
+  private static final int ERROR_CORNER_TO_MISSING = -5;
+  private static final int ERROR_CORNER_BO_MISSING = -6;
+
+  private static final int ERROR_CENTER_0_MISSING = -7;
+  private static final int ERROR_CENTER_1_MISSING = -8;
+  private static final int ERROR_CENTER_2_MISSING = -9;
+  private static final int ERROR_CENTER_3_MISSING = -10;
+  private static final int ERROR_CENTER_4_MISSING = -11;
+  private static final int ERROR_CENTER_5_MISSING = -12;
+  private static final int ERROR_CENTER_6_MISSING = -13;
+  private static final int ERROR_CENTER_7_MISSING = -14;
+
+  private static final int ERROR_TWO_CENTERS      = -15;
+  private static final int ERROR_CORNERS_CANNOT   = -16;
+
+  private TablebasesAbstract mSolver;
+  private final int[] mFaceColors;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public SolverPyraminxDiamond(Resources res, TwistyObject object)
+    {
+    super(res,object);
+    mFaceColors = new int[8];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int retCenter(int color, int[] centers)
+    {
+    for(int i=0; i<8; i++ )
+      if( centers[i]==color ) return i;
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[] getCentersPermutation(int[] centers)
+    {
+    int[] perm = new int[8];
+
+    for(int i=0; i<8; i++ )
+      perm[i] = retCenter(mFaceColors[i],centers);
+
+    return perm;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private boolean isTwistEven(int[] twist)
+    {
+    int total = twist[0]+twist[1]+twist[2]+twist[3]+twist[4]+twist[5];
+    return ((total%2)==0);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int indexOf(int[] corner, int color )
+    {
+    if( corner[0]==color ) return 0;
+    if( corner[1]==color ) return 1;
+    if( corner[2]==color ) return 2;
+    if( corner[3]==color ) return 3;
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[] getCornersTwist(int[][] corners)
+    {
+    int[] twist = new int[6];
+
+    twist[0] = indexOf(corners[0],mFaceColors[0]);
+    twist[1] = indexOf(corners[1],mFaceColors[5]);
+    twist[2] = indexOf(corners[2],mFaceColors[2]);
+    twist[3] = indexOf(corners[3],mFaceColors[4]);
+    twist[4] = indexOf(corners[4],mFaceColors[2]);
+    twist[5] = indexOf(corners[5],mFaceColors[6]);
+
+
+    return twist;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int checkAllColorsDifferent()
+    {
+    for(int i=0; i<8; i++)
+      {
+      boolean present = false;
+
+      for(int j=0; j<8; j++)
+        if( mFaceColors[j]==i )
+          {
+          present=true;
+          break;
+          }
+
+      if( !present ) return ERROR_CORNERS_CANNOT;
+      }
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int checkAllCentersPresent(int[] centers)
+    {
+    for(int i=0; i<8; i++)
+      {
+      boolean present = false;
+
+      for(int j=0; j<8; j++)
+        if( centers[j]==i )
+          {
+          present=true;
+          break;
+          }
+
+      if( !present )
+        {
+        switch(i)
+          {
+          case 0: return ERROR_CENTER_0_MISSING;
+          case 1: return ERROR_CENTER_1_MISSING;
+          case 2: return ERROR_CENTER_2_MISSING;
+          case 3: return ERROR_CENTER_3_MISSING;
+          case 4: return ERROR_CENTER_4_MISSING;
+          case 5: return ERROR_CENTER_5_MISSING;
+          case 6: return ERROR_CENTER_6_MISSING;
+          case 7: return ERROR_CENTER_7_MISSING;
+          }
+        }
+      }
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int commonColor(int[][] corners, int index1, int index2, int index3)
+    {
+    int[] c1 = corners[index1];
+    int[] c2 = corners[index2];
+    int[] c3 = corners[index3];
+
+    for(int i=0; i<4; i++)
+      {
+      int c = c1[i];
+
+      if( (c2[0]==c || c2[1]==c || c2[2]==c || c2[3]==c) &&
+          (c3[0]==c || c3[1]==c || c3[2]==c || c3[3]==c)  ) return c;
+      }
+
+    return ERROR_CORNERS_CANNOT;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int figureOutFaceColors(int[] output, int[][] corners)
+    {
+    int[][] commonCorners = {{0,2,4},{0,3,5},{1,2,4},{1,3,5},{0,2,3},{1,2,3},{0,4,5},{1,4,5}};
+
+    for(int i=0; i<8; i++)
+      {
+      int[] common = commonCorners[i];
+      output[i] = commonColor(corners,common[0],common[1],common[2]);
+      if( output[i]<0 ) return output[i];
+      }
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void getCorners(TwistyObject object, int[][] corners)
+    {
+    for(int i=0; i<6; i++)
+      for(int j=0; j<4; j++)
+         corners[i][j] = object.getCubitFaceStickerIndex(i,j);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void getCenters(TwistyObject object, int[] centers)
+    {
+    for(int i=0; i<8; i++)
+       centers[i] = object.getCubitFaceStickerIndex(6+i,0)-8;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int tablebaseIndex(TwistyObject object)
+    {
+    int[][] corners = new int[6][4];
+    int[] centers = new int[8];
+
+    getCorners(object,corners);
+    getCenters(object,centers);
+
+    int result1 = figureOutFaceColors(mFaceColors,corners);
+    if( result1<0 ) return result1;
+
+    int result2 = checkAllCentersPresent(centers);
+    if( result2<0 ) return result2;
+
+    int result3 = checkAllColorsDifferent();
+    if( result3<0 ) return result3;
+
+    int[] twist = getCornersTwist(corners);
+    boolean even1 = isTwistEven(twist);
+
+    int[] centers_perm = getCentersPermutation(centers);
+    boolean even2 = TablebaseHelpers.permutationIsEven(centers_perm);
+    if( even1^even2 ) return ERROR_TWO_CENTERS;
+
+    int centers_perm_num = TablebaseHelpers.computePermutationNum(centers_perm);
+    int total_twist = twist[0]+ 4*(twist[1]+ 4*(twist[2]+ 4*(twist[3]+ 4*(twist[4]+ 4*(twist[5]>1 ? 0:1)))));
+
+/*
+android.util.Log.e("D", "faces: "+mFaceColors[0]+" "+mFaceColors[1]+" "+mFaceColors[2]+" "
++mFaceColors[3]+" "+mFaceColors[4]+" "+mFaceColors[5]+" "+mFaceColors[6]+" "+mFaceColors[7]);
+android.util.Log.e("D", "corn twist: "+twist[0]+" "+twist[1]+" "+twist[2]+" "+twist[3]+" "+twist[4]+" "+twist[5]);
+android.util.Log.e("D", "ret="+(total_twist + 2048*centers_perm_num) );
+*/
+    return total_twist + 2048*centers_perm_num;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getColorIndex4(int face)
+    {
+    switch(mFaceColors[face])
+      {
+      case 0: return R.string.color_violet4;
+      case 1: return R.string.color_grey4;
+      case 2: return R.string.color_blue4;
+      case 3: return R.string.color_red4;
+      case 4: return R.string.color_orange4;
+      case 5: return R.string.color_green4;
+      case 6: return R.string.color_white4;
+      case 7: return R.string.color_yellow4;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getColorIndex3(int face)
+    {
+    switch(mFaceColors[face])
+      {
+      case 0: return R.string.color_violet3;
+      case 1: return R.string.color_grey3;
+      case 2: return R.string.color_blue3;
+      case 3: return R.string.color_red3;
+      case 4: return R.string.color_orange3;
+      case 5: return R.string.color_green3;
+      case 6: return R.string.color_white3;
+      case 7: return R.string.color_yellow3;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getColorIndex2(int face)
+    {
+    switch(face)
+      {
+      case 0: return R.string.color_violet2;
+      case 1: return R.string.color_grey2;
+      case 2: return R.string.color_blue2;
+      case 3: return R.string.color_red2;
+      case 4: return R.string.color_orange2;
+      case 5: return R.string.color_green2;
+      case 6: return R.string.color_white2;
+      case 7: return R.string.color_yellow2;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String centerError(Resources res, int face)
+    {
+    String color = res.getString(getColorIndex2(face));
+    return res.getString(R.string.solver_generic_missing_center,color);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String cornerError(Resources res, int f1, int f2)
+    {
+    String c1 = res.getString(getColorIndex3(f1));
+    String c2 = res.getString(getColorIndex4(f2));
+    return res.getString(R.string.solver_generic_missing_corner2,c1,c2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String error(int index, Resources res)
+    {
+    switch(index)
+      {
+      case ERROR_CORNER_FR_MISSING: return cornerError(res,1,4);
+      case ERROR_CORNER_BR_MISSING: return cornerError(res,1,6);
+      case ERROR_CORNER_BL_MISSING: return cornerError(res,3,6);
+      case ERROR_CORNER_FL_MISSING: return cornerError(res,3,4);
+      case ERROR_CORNER_TO_MISSING: return cornerError(res,1,3);
+      case ERROR_CORNER_BO_MISSING: return cornerError(res,4,6);
+
+      case ERROR_CENTER_0_MISSING : return centerError(res,0);
+      case ERROR_CENTER_1_MISSING : return centerError(res,1);
+      case ERROR_CENTER_2_MISSING : return centerError(res,2);
+      case ERROR_CENTER_3_MISSING : return centerError(res,3);
+      case ERROR_CENTER_4_MISSING : return centerError(res,4);
+      case ERROR_CENTER_5_MISSING : return centerError(res,5);
+      case ERROR_CENTER_6_MISSING : return centerError(res,6);
+      case ERROR_CENTER_7_MISSING : return centerError(res,7);
+
+      case ERROR_TWO_CENTERS      : return res.getString(R.string.solver_generic_two_centers);
+      case ERROR_CORNERS_CANNOT   : return res.getString(R.string.solver_generic_corners_cannot);
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[][] solution(int index, Resources res)
+    {
+    if( mSolver==null )
+      {
+      mSolver = ImplementedTablebasesList.createUnpacked(ObjectSignatures.PDIA_3);
+
+      if( mSolver!=null )
+        {
+        mSolver.createTablebase(2);
+        }
+      }
+
+    return mSolver!=null ? mSolver.solution(index,null) : null;
+    }
+}  
+
