commit d0852c9cff836c3afa2e429876c8fbdfadfcc250
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Apr 10 21:57:12 2023 +0200

    Beginnings of CU_323 solver.

diff --git a/src/main/java/org/distorted/solvers/ImplementedSolversList.java b/src/main/java/org/distorted/solvers/ImplementedSolversList.java
index c577327c..45f408b3 100644
--- a/src/main/java/org/distorted/solvers/ImplementedSolversList.java
+++ b/src/main/java/org/distorted/solvers/ImplementedSolversList.java
@@ -22,7 +22,8 @@ public enum ImplementedSolversList
   SKEWB          (ObjectSignatures.SKEW_2, R.string.solver_skew2_title, R.string.solver_skew2_description, true),
   PYRAMINX_DUO   (ObjectSignatures.PDUO_2, R.string.solver_pduo2_title, R.string.solver_pduo2_description, true),
   IVY            (ObjectSignatures.IVY_2 , R.string.solver_ivy_title, R.string.solver_ivy_description, true),
-  TOWER          (ObjectSignatures.CU_232, R.string.solver_cu232_title, R.string.solver_cu232_description, true),
+  CU_232         (ObjectSignatures.CU_232, R.string.solver_cu232_title, R.string.solver_cu232_description, true),
+  CU_323         (ObjectSignatures.CU_323, R.string.solver_cu323_title, R.string.solver_cu323_description, true),
   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),
diff --git a/src/main/java/org/distorted/solvers/SolverCuboid323.java b/src/main/java/org/distorted/solvers/SolverCuboid323.java
new file mode 100644
index 00000000..01da6e2d
--- /dev/null
+++ b/src/main/java/org/distorted/solvers/SolverCuboid323.java
@@ -0,0 +1,433 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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.helpers.OperatingSystemInterface;
+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 SolverCuboid323 extends SolverTablebase
+{
+  private static final int ERROR_CORNER_MISSING = -1;
+  private static final int ERROR_EDGE_MISSING   = -2;
+  private static final int ERROR_CORNERS_CANNOT = -3;
+  private static final int ERROR_EDGE_TWISTED   = -4;
+  private static final int ERROR_CORNER_TWISTED = -5;
+
+  TablebasesAbstract mSolver;
+  private final int[] mFaceColors;
+  private int mErrorColor1, mErrorColor2, mErrorColor3;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int edgeIs(int[] edge, int i0, int i1)
+    {
+    int c0 = mFaceColors[i0];
+    int c1 = mFaceColors[i1];
+
+    if( edge[0]==c0 && edge[1]==c1 ) return 0;
+    if( edge[0]==c1 && edge[1]==c0 ) return 1;
+    return 2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int retEdgePermutation(int[] output, int[][] edges)
+    {
+    for(int i=0; i<4; i++) output[i] = -1;
+
+    for(int i=0; i<4; i++)
+      {
+      int edge0 = edgeIs(edges[i],1,5);
+           if( edge0==0 ) output[0]=i;
+      else if( edge0==1 ) return ERROR_EDGE_TWISTED;
+
+      int edge1 = edgeIs(edges[i],1,4);
+           if( edge1==0 ) output[1]=i;
+      else if( edge1==1 ) return ERROR_EDGE_TWISTED;
+
+      int edge2 = edgeIs(edges[i],0,5);
+           if( edge2==0 ) output[2]=i;
+      else if( edge2==1 ) return ERROR_EDGE_TWISTED;
+
+      int edge3 = edgeIs(edges[i],0,4);
+           if( edge3==0 ) output[3]=i;
+      else if( edge3==1 ) return ERROR_EDGE_TWISTED;
+      }
+/*
+    if( output[0]==-1 ) return ERROR_EDGE_15_MISSING;
+    if( output[1]==-1 ) return ERROR_EDGE_14_MISSING;
+    if( output[2]==-1 ) return ERROR_EDGE_05_MISSING;
+    if( output[3]==-1 ) return ERROR_EDGE_04_MISSING;
+*/
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int cornerIs(int[] corner, int i0, int i1, int i2)
+    {
+    int c0 = mFaceColors[i0];
+    int c1 = mFaceColors[i1];
+    int c2 = mFaceColors[i2];
+
+    if( corner[0]==c0 && corner[1]==c1 && corner[2]==c2 ) return 0;
+
+    if( corner[0]==c0 && corner[1]==c2 && corner[2]==c1 ||
+        corner[0]==c1 && corner[1]==c0 && corner[2]==c2 ||
+        corner[0]==c1 && corner[1]==c2 && corner[2]==c0 ||
+        corner[0]==c2 && corner[1]==c0 && corner[2]==c1 ||
+        corner[0]==c2 && corner[1]==c1 && corner[2]==c0  ) return 1;
+
+    return 2;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int retCornerPermutation(int[] output, int[][] corners)
+    {
+    for(int i=0; i<8; i++) output[i] = -1;
+
+    for(int i=0; i<8; i++)
+      {
+      int corner7 = cornerIs(corners[i],2,4,0);
+           if( corner7==0 ) output[7]=i;
+      else if( corner7==1 ) return ERROR_CORNER_TWISTED;
+
+      int corner6 = cornerIs(corners[i],2,0,5);
+           if( corner6==0 ) output[6]=i;
+      else if( corner6==1 ) return ERROR_CORNER_TWISTED;
+
+      int corner5 = cornerIs(corners[i],3,0,4);
+           if( corner5==0 ) output[5]=i;
+      else if( corner5==1 ) return ERROR_CORNER_TWISTED;
+
+      int corner4 = cornerIs(corners[i],3,5,0);
+           if( corner4==0 ) output[4]=i;
+      else if( corner4==1 ) return ERROR_CORNER_TWISTED;
+
+      int corner3 = cornerIs(corners[i],2,1,4);
+           if( corner3==0 ) output[3]=i;
+      else if( corner3==1 ) return ERROR_CORNER_TWISTED;
+
+      int corner2 = cornerIs(corners[i],2,5,1);
+           if( corner2==0 ) output[2]=i;
+      else if( corner2==1 ) return ERROR_CORNER_TWISTED;
+
+      int corner1 = cornerIs(corners[i],3,4,1);
+           if( corner1==0 ) output[1]=i;
+      else if( corner1==1 ) return ERROR_CORNER_TWISTED;
+
+      int corner0 = cornerIs(corners[i],3,1,5);
+           if( corner0==0 ) output[0]=i;
+      else if( corner0==1 ) return ERROR_CORNER_TWISTED;
+      }
+/*
+    if( output[0]==-1 ) return ERROR_CORNER_135_MISSING;
+    if( output[1]==-1 ) return ERROR_CORNER_134_MISSING;
+    if( output[2]==-1 ) return ERROR_CORNER_125_MISSING;
+    if( output[3]==-1 ) return ERROR_CORNER_124_MISSING;
+    if( output[4]==-1 ) return ERROR_CORNER_035_MISSING;
+    if( output[5]==-1 ) return ERROR_CORNER_034_MISSING;
+    if( output[6]==-1 ) return ERROR_CORNER_025_MISSING;
+    if( output[7]==-1 ) return ERROR_CORNER_024_MISSING;
+*/
+    return 0;
+    }
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+  private int computeFaceColors(int[][] corners, int[][] edges, int[] centers)
+    {
+    mFaceColors[2] = centers[0];
+    mFaceColors[3] = centers[1];
+
+         if( edges[1][1]==mFaceColors[2] ) mFaceColors[4] = edges[1][0];
+    else if( edges[1][1]==mFaceColors[2] ) mFaceColors[4] = edges[1][0];
+
+         if( edges[0][0]==mFaceColors[1] ) mFaceColors[5] = edges[0][1];
+    else if( edges[2][0]==mFaceColors[1] ) mFaceColors[5] = edges[2][1];
+    else if( edges[3][0]==mFaceColors[1] ) mFaceColors[5] = edges[3][1];
+    else return ERROR_EDGE_TWISTED;
+
+         if( edges[0][1]==mFaceColors[4] ) mFaceColors[0] = edges[0][0];
+    else if( edges[2][1]==mFaceColors[4] ) mFaceColors[0] = edges[2][0];
+    else if( edges[3][1]==mFaceColors[4] ) mFaceColors[0] = edges[3][0];
+    else return ERROR_EDGE_TWISTED;
+
+    boolean found2 = false;
+    boolean found3 = false;
+
+    for(int c=0; c<8; c++)
+      {
+      if( !found3 && corners[c][1]==mFaceColors[4] && corners[c][2]==mFaceColors[1] )
+        {
+        found3=true;
+        mFaceColors[3] = corners[c][0];
+        }
+      if( !found2 && corners[c][1]==mFaceColors[1] && corners[c][2]==mFaceColors[4] )
+        {
+        found2=true;
+        mFaceColors[2] = corners[c][0];
+        }
+      }
+
+    if( !found2 || !found3 ) return ERROR_CORNERS_CANNOT;
+
+    for(int i=0; i<6; i++)
+      for(int j=i+1; j<6; j++)
+        if( mFaceColors[i]==mFaceColors[j] ) return ERROR_CORNERS_CANNOT;
+
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int[] correctEdgePerm(int[] perm)
+    {
+    int[] ret = new int[3];
+
+    ret[0] = perm[0];
+    ret[1] = perm[2];
+    ret[2] = perm[3];
+
+    if( ret[0]>1 ) ret[0]--;
+    if( ret[1]>1 ) ret[1]--;
+    if( ret[2]>1 ) ret[2]--;
+
+    return ret;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public SolverCuboid323(OperatingSystemInterface os, Resources res, TwistyObject object)
+    {
+    super(os,res,object);
+    mFaceColors = new int[6];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void getCorners(TwistyObject object, int[][] corners)
+    {
+    corners[0][0] = object.getCubitFaceStickerIndex(0,1);
+    corners[0][1] = object.getCubitFaceStickerIndex(0,3);
+    corners[0][2] = object.getCubitFaceStickerIndex(0,5);
+
+    corners[1][0] = object.getCubitFaceStickerIndex(1,3);
+    corners[1][1] = object.getCubitFaceStickerIndex(1,1);
+    corners[1][2] = object.getCubitFaceStickerIndex(1,5);
+
+    corners[2][0] = object.getCubitFaceStickerIndex(2,5);
+    corners[2][1] = object.getCubitFaceStickerIndex(2,3);
+    corners[2][2] = object.getCubitFaceStickerIndex(2,1);
+
+    corners[3][0] = object.getCubitFaceStickerIndex(3,1);
+    corners[3][1] = object.getCubitFaceStickerIndex(3,3);
+    corners[3][2] = object.getCubitFaceStickerIndex(3,5);
+
+    corners[4][0] = object.getCubitFaceStickerIndex(4,1);
+    corners[4][1] = object.getCubitFaceStickerIndex(4,5);
+    corners[4][2] = object.getCubitFaceStickerIndex(4,3);
+
+    corners[5][0] = object.getCubitFaceStickerIndex(5,1);
+    corners[5][1] = object.getCubitFaceStickerIndex(5,3);
+    corners[5][2] = object.getCubitFaceStickerIndex(5,5);
+
+    corners[6][0] = object.getCubitFaceStickerIndex(6,1);
+    corners[6][1] = object.getCubitFaceStickerIndex(6,3);
+    corners[6][2] = object.getCubitFaceStickerIndex(6,5);
+
+    corners[7][0] = object.getCubitFaceStickerIndex(7,1);
+    corners[7][1] = object.getCubitFaceStickerIndex(7,5);
+    corners[7][2] = object.getCubitFaceStickerIndex(7,3);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void getEdges(TwistyObject object, int[][] edges)
+    {
+    edges[0][0] = object.getCubitFaceStickerIndex(8,5);
+    edges[0][1] = object.getCubitFaceStickerIndex(8,3);
+    edges[1][0] = object.getCubitFaceStickerIndex(9,3);
+    edges[1][1] = object.getCubitFaceStickerIndex(9,5);
+    edges[2][0] = object.getCubitFaceStickerIndex(10,3);
+    edges[2][1] = object.getCubitFaceStickerIndex(10,5);
+    edges[3][0] = object.getCubitFaceStickerIndex(11,5);
+    edges[3][1] = object.getCubitFaceStickerIndex(11,3);
+    edges[4][0] = object.getCubitFaceStickerIndex(12,3);
+    edges[4][1] = object.getCubitFaceStickerIndex(12,5);
+    edges[5][0] = object.getCubitFaceStickerIndex(13,5);
+    edges[5][1] = object.getCubitFaceStickerIndex(13,3);
+    edges[6][0] = object.getCubitFaceStickerIndex(14,3);
+    edges[6][1] = object.getCubitFaceStickerIndex(14,5);
+    edges[7][0] = object.getCubitFaceStickerIndex(15,5);
+    edges[7][1] = object.getCubitFaceStickerIndex(15,3);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void getCenters(TwistyObject object, int[] centers)
+    {
+    centers[0] = object.getCubitFaceStickerIndex(16,4);
+    centers[1] = object.getCubitFaceStickerIndex(17,4);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int tablebaseIndex(TwistyObject object)
+    {
+    int[][] corners= new int[8][3];
+    int[][] edges  = new int[8][2];
+    int[] centers  = new int[2];
+
+    getCorners(object,corners);
+    getEdges(object,edges);
+    getCenters(object,centers);
+
+    int result0 = computeFaceColors(corners, edges, centers);
+    if( result0<0 ) return result0;
+/*
+    int[] corner_perm = new int[8];
+    int result1 = retCornerPermutation(corner_perm,corners);
+    if( result1<0 ) return result1;
+
+    int[] edge_perm = new int[8];
+    int result2 = retEdgePermutation(edge_perm,edges);
+    if( result2<0 ) return result2;
+
+    int[] edge_perm2 = correctEdgePerm(edge_perm); // edge1 is fixed!
+
+    int corner_perm_num = TablebaseHelpers.computePermutationNum(corner_perm);
+    int edge_perm_num = TablebaseHelpers.computePermutationNum(edge_perm2);
+
+    return edge_perm_num + 6*corner_perm_num;
+
+ */
+    return 0;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getColorIndex4(int face)
+    {
+    switch(mFaceColors[face])
+      {
+      case 0: return R.string.color_yellow4;
+      case 1: return R.string.color_white4;
+      case 2: return R.string.color_blue4;
+      case 3: return R.string.color_green4;
+      case 4: return R.string.color_red4;
+      case 5: return R.string.color_orange4;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getColorIndex3(int face)
+    {
+    switch(mFaceColors[face])
+      {
+      case 0: return R.string.color_yellow3;
+      case 1: return R.string.color_white3;
+      case 2: return R.string.color_blue3;
+      case 3: return R.string.color_green3;
+      case 4: return R.string.color_red3;
+      case 5: return R.string.color_orange3;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int getColorIndex6(int face)
+    {
+    switch(mFaceColors[face])
+      {
+      case 0: return R.string.color_yellow6;
+      case 1: return R.string.color_white6;
+      case 2: return R.string.color_blue6;
+      case 3: return R.string.color_green6;
+      case 4: return R.string.color_red6;
+      case 5: return R.string.color_orange6;
+      }
+
+    return -1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String edgeError(Resources res, int face0, int face1)
+    {
+    int j0 = getColorIndex3(face0);
+    int j1 = getColorIndex6(face1);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+
+    return res.getString(R.string.solver_generic_missing_edge,c0,c1);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String cornerError(Resources res, int face0, int face1, int face2)
+    {
+    int j0 = getColorIndex3(face0);
+    int j1 = getColorIndex3(face1);
+    int j2 = getColorIndex4(face2);
+
+    String c0 = res.getString(j0);
+    String c1 = res.getString(j1);
+    String c2 = res.getString(j2);
+
+    return res.getString(R.string.solver_generic_missing_corner,c0,c1,c2);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String error(int index, Resources res)
+    {
+    switch(index)
+      {
+      case ERROR_CORNER_MISSING : return cornerError(res,mErrorColor1,mErrorColor2,mErrorColor3);
+      case ERROR_EDGE_MISSING   : return edgeError(res,mErrorColor1,mErrorColor2);
+      case ERROR_CORNERS_CANNOT : return res.getString(R.string.solver_generic_corners_cannot);
+      case ERROR_EDGE_TWISTED   : return res.getString(R.string.solver_generic_edge_twist);
+      case ERROR_CORNER_TWISTED : return res.getString(R.string.solver_generic_corner_twist);
+      }
+
+    return null;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[][] solution(int index, OperatingSystemInterface os)
+    {
+    if( mSolver==null )
+      {
+      mSolver = ImplementedTablebasesList.createUnpacked(ObjectSignatures.CU_323);
+      if( mSolver!=null ) mSolver.createTablebase(2);
+      }
+
+    return mSolver!=null ? mSolver.solution(index,null,os) : null;
+    }
+}  
+
diff --git a/src/main/java/org/distorted/solvers/SolverMain.java b/src/main/java/org/distorted/solvers/SolverMain.java
index 1c122f38..18590877 100644
--- a/src/main/java/org/distorted/solvers/SolverMain.java
+++ b/src/main/java/org/distorted/solvers/SolverMain.java
@@ -94,6 +94,11 @@ public class SolverMain implements Runnable
       SolverTablebase solver = new SolverCuboid232(mOS,mRes,mObject);
       solver.solve(screen);
       }
+    else if( mSignature==ObjectSignatures.CU_323 )
+      {
+      SolverTablebase solver = new SolverCuboid323(mOS,mRes,mObject);
+      solver.solve(screen);
+      }
     else if( mSignature==ObjectSignatures.PYRA_3 )
       {
       SolverTablebase solver = new SolverPyraminx(mOS,mRes,mObject);
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 2aec4928..11001109 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -198,6 +198,7 @@
     <string name="solver_pyra3_title" translatable="false">Pyraminx Solver</string>
     <string name="solver_ivy_title" translatable="false">Ivy Solver</string>
     <string name="solver_cu232_title" translatable="false">2x2x3 Cuboid Solver</string>
+    <string name="solver_cu323_title" translatable="false">3x3x2 Cuboid Solver</string>
     <string name="solver_diam2_title" translatable="false">Skewb Diamond Solver</string>
     <string name="solver_cube2_title" translatable="false">2x2 Cube Solver</string>
     <string name="solver_skew2_title" translatable="false">Skewb Solver</string>
