Project

General

Profile

Download (19 KB) Statistics
| Branch: | Revision:

distorted-objectlib / src / main / java / org / distorted / objectlib / helpers / ObjectSignature.java @ e342c3f7

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2022 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.objectlib.helpers;
21

    
22
import org.distorted.objectlib.main.ObjectType;
23

    
24
import java.util.ArrayList;
25

    
26
///////////////////////////////////////////////////////////////////////////////////////////////////
27

    
28
public class ObjectSignature implements Comparable<ObjectSignature>
29
{
30
  private long mSignature1, mSignature2, mSignature3;
31
  private int[] mLayer;
32
  private int[][][][] mCycles;
33
  private float[][] mCubitTouch;
34
  private int mNumCubitTouches;
35
  private int[] mNumLeftCyclesPerLayer;
36
  private int[] mNumCentCyclesPerLayer;
37
  private int[] mNumInneCyclesPerLayer;
38

    
39
///////////////////////////////////////////////////////////////////////////////////////////////////
40
// built-in objects
41

    
42
  public ObjectSignature(ObjectType type)
43
    {
44
    mSignature1 = 0;
45
    mSignature2 = 0;
46
    mSignature3 = type.ordinal();
47
    }
48

    
49
///////////////////////////////////////////////////////////////////////////////////////////////////
50

    
51
  public ObjectSignature(ObjectSignature sig)
52
    {
53
    mSignature1 = sig.mSignature1;
54
    mSignature2 = sig.mSignature2;
55
    mSignature3 = sig.mSignature3;
56

    
57
    mLayer      = sig.mLayer;
58
    mCycles     = sig.mCycles;
59
    mCubitTouch = sig.mCubitTouch;
60

    
61
    mNumCubitTouches       = sig.mNumCubitTouches;
62
    mNumCentCyclesPerLayer = sig.mNumCentCyclesPerLayer;
63
    mNumLeftCyclesPerLayer = sig.mNumLeftCyclesPerLayer;
64
    mNumInneCyclesPerLayer = sig.mNumInneCyclesPerLayer;
65
    }
66

    
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68
// built-in bandaged 3x3s; objects created from JSON (version1)
69

    
70
  public ObjectSignature(long signature)
71
    {
72
    mSignature1 = 0;
73
    mSignature2 = 0;
74
    mSignature3 = signature;
75
    }
76

    
77
///////////////////////////////////////////////////////////////////////////////////////////////////
78
// locally created bandaged cuboids created from JSON (version2)
79

    
80
  public ObjectSignature(String shortName, long signature1, long signature2, long signature3)
81
    {
82
    mSignature1 = signature1;
83
    mSignature2 = signature2;
84
    mSignature3 = signature3;
85

    
86
    int x = shortName.charAt(0) - '0';
87
    int y = shortName.charAt(1) - '0';
88
    int z = shortName.charAt(2) - '0';
89

    
90
    mLayer = new int[] {x,y,z};
91

    
92
    prepareCubitTouch();
93
    prepareAllCycles();
94
    }
95

    
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97
// other objects created from JSON (version2)
98

    
99
  public ObjectSignature(long signature1, long signature2, long signature3)
100
    {
101
    mSignature1 = signature1;
102
    mSignature2 = signature2;
103
    mSignature3 = signature3;
104
    }
105

    
106
///////////////////////////////////////////////////////////////////////////////////////////////////
107
// Locally created bandaged cuboids 1<=N,M,K<=5
108
// This is the 'Andreas signature' of a bandaged cube.
109
// https://twistypuzzles.com/forum/viewtopic.php?p=415466#p415466
110

    
111
  public ObjectSignature(int lenx, int leny, int lenz, float[][] position)
112
    {
113
    mLayer = new int[] {lenx,leny,lenz};
114

    
115
    prepareCubitTouch();
116
    prepareAllCycles();
117

    
118
    for(float[] pos : position)
119
      {
120
      int numCenters = pos.length/3;
121

    
122
      for(int i=0; i<numCenters; i++)
123
        {
124
        float xi = pos[3*i  ];
125
        float yi = pos[3*i+1];
126
        float zi = pos[3*i+2];
127

    
128
        for(int j=i+1; j<numCenters; j++)
129
          {
130
          float xj = pos[3*j  ];
131
          float yj = pos[3*j+1];
132
          float zj = pos[3*j+2];
133

    
134
          if(areNeighbours(xi-xj,yi-yj,zi-zj))
135
            {
136
            float xc = (xi+xj)/2;
137
            float yc = (yi+yj)/2;
138
            float zc = (zi+zj)/2;
139

    
140
            int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
141
            setBit(bitIndex,1);
142
            }
143
          }
144
        }
145
      }
146
    }
147

    
148
///////////////////////////////////////////////////////////////////////////////////////////////////
149

    
150
  public void setSignature(ObjectSignature sig)
151
    {
152
    mSignature1 = sig.mSignature1;
153
    mSignature2 = sig.mSignature2;
154
    mSignature3 = sig.mSignature3;
155
    }
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158

    
159
  public void setSignature(int sig)
160
    {
161
    mSignature1 = 0;
162
    mSignature2 = 0;
163
    mSignature3 = sig;
164
    }
165

    
166
///////////////////////////////////////////////////////////////////////////////////////////////////
167

    
168
  public int compareTo(ObjectSignature sig)
169
    {
170
         if( mSignature1>sig.mSignature1 ) return +1;
171
    else if( mSignature1<sig.mSignature1 ) return -1;
172
    else
173
      {
174
           if( mSignature2>sig.mSignature2 ) return +1;
175
      else if( mSignature2<sig.mSignature2 ) return -1;
176
      else
177
        {
178
             if( mSignature3>sig.mSignature3 ) return +1;
179
        else if( mSignature3<sig.mSignature3 ) return -1;
180
        else                                   return  0;
181
        }
182
      }
183
    }
184

    
185
///////////////////////////////////////////////////////////////////////////////////////////////////
186

    
187
  public boolean isEqual(ObjectSignature sig)
188
    {
189
    return mSignature1==sig.mSignature1 && mSignature2==sig.mSignature2 && mSignature3==sig.mSignature3;
190
    }
191

    
192
///////////////////////////////////////////////////////////////////////////////////////////////////
193

    
194
  public long getLong1()
195
    {
196
    return mSignature1;
197
    }
198

    
199
///////////////////////////////////////////////////////////////////////////////////////////////////
200

    
201
  public long getLong2()
202
    {
203
    return mSignature2;
204
    }
205

    
206
///////////////////////////////////////////////////////////////////////////////////////////////////
207

    
208
  public long getLong3()
209
    {
210
    return mSignature3;
211
    }
212

    
213
///////////////////////////////////////////////////////////////////////////////////////////////////
214

    
215
  public String getString()
216
    {
217
    String sig1 = String.format("0x%016X", mSignature1);
218
    String sig2 = String.format("0x%016X", mSignature2);
219
    String sig3 = String.format("0x%016X", mSignature3);
220

    
221
    return sig1+"_"+sig2+"_"+sig3;
222
    }
223

    
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

    
226
  public boolean isUnblockedFromLeft(int axis, int layer)
227
    {
228
    if(layer>0)
229
      for(int index=0; index<mNumCubitTouches; index++)
230
        if( getBit(index)!=0 )
231
          {
232
          float[] touch = getCubitTouchOfIndex(index);
233
          if( belongsLeft(touch,axis,layer) ) return false;
234
          }
235

    
236
    return true;
237
    }
238

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240

    
241
  public ObjectSignature turn(int axis, int layer, int turn)
242
    {
243
    ObjectSignature ret = new ObjectSignature(this);
244

    
245
    // I don't understand it, but Firebase shows mCycles is occasionally null here.
246
    if( mCycles!=null && mCycles[axis]!=null )
247
      {
248
      int[][] cycles = mCycles[axis][layer];
249

    
250
      // it can happen that there are no cycles in this layer: 2x1x2 axis 0 layer 0.
251
      if( cycles!=null && cycles.length>0 && cycles[0]!=null )
252
        {
253
        if( cycles[0].length==4 ) for(int[] cyc : cycles) ret.cycle4(turn,cyc);
254
        else                      for(int[] cyc : cycles) ret.cycle2(cyc);
255
        }
256
      }
257

    
258
    return ret;
259
    }
260

    
261
///////////////////////////////////////////////////////////////////////////////////////////////////
262

    
263
  private boolean belongsLeft(float[] point, int axis, int layer)
264
    {
265
    return 2*point[axis]+mLayer[axis] == 2*layer;
266
    }
267

    
268
///////////////////////////////////////////////////////////////////////////////////////////////////
269

    
270
  private void cycle2(int[] cyc)
271
    {
272
    int index0 = cyc[0];
273
    int index1 = cyc[1];
274

    
275
    long b0 = getBit(index0);
276
    long b1 = getBit(index1);
277

    
278
    setBit(index1,b0);
279
    setBit(index0,b1);
280
    }
281

    
282
///////////////////////////////////////////////////////////////////////////////////////////////////
283

    
284
  private void cycle4(int turn, int[] cyc)
285
    {
286
    int index0 = cyc[0];
287
    int index1 = cyc[1];
288
    int index2 = cyc[2];
289
    int index3 = cyc[3];
290

    
291
    long b0 = getBit(index0);
292
    long b1 = getBit(index1);
293
    long b2 = getBit(index2);
294
    long b3 = getBit(index3);
295

    
296
    switch(turn)
297
      {
298
      case 1: setBit(index0,b3);
299
              setBit(index1,b0);
300
              setBit(index2,b1);
301
              setBit(index3,b2);
302
              break;
303
      case 2: setBit(index0,b2);
304
              setBit(index1,b3);
305
              setBit(index2,b0);
306
              setBit(index3,b1);
307
              break;
308
      case 3: setBit(index0,b1);
309
              setBit(index1,b2);
310
              setBit(index2,b3);
311
              setBit(index3,b0);
312
              break;
313
      }
314
    }
315

    
316
///////////////////////////////////////////////////////////////////////////////////////////////////
317

    
318
  private void prepareCubitTouch()
319
    {
320
    int numCenters = mLayer[0]*mLayer[1]*mLayer[2];
321
    if( mLayer[0]>1 && mLayer[1]>1 && mLayer[2]>1 ) numCenters -= (mLayer[0]-2)*(mLayer[1]-2)*(mLayer[2]-2);
322

    
323
    float[][] centers = new float[numCenters][];
324
    int index = 0;
325

    
326
    for(int i=0; i<mLayer[0]; i++)
327
      for(int j=0; j<mLayer[1]; j++)
328
        for(int k=0; k<mLayer[2]; k++)
329
          if( (i==0) || (i==mLayer[0]-1) || (j==0) || (j==mLayer[1]-1) || (k==0) || (k==mLayer[2]-1) )
330
            {
331
            centers[index++] = new float[] { i+0.5f*(1-mLayer[0]), j+0.5f*(1-mLayer[1]), k+0.5f*(1-mLayer[2]) };
332
            }
333

    
334
    ArrayList<float[]> mTouch = new ArrayList<>();
335

    
336
    for(int i=0; i<numCenters; i++)
337
      for(int j=i+1; j<numCenters; j++)
338
        {
339
        float[] c0 = centers[i];
340
        float[] c1 = centers[j];
341

    
342
        float x1 = c0[0];
343
        float y1 = c0[1];
344
        float z1 = c0[2];
345
        float x2 = c1[0];
346
        float y2 = c1[1];
347
        float z2 = c1[2];
348

    
349
        if( areNeighbours(x1-x2,y1-y2,z1-z2) )
350
          {
351
          float xc = (x1+x2)/2;
352
          float yc = (y1+y2)/2;
353
          float zc = (z1+z2)/2;
354

    
355
          float[] touch = new float[] {xc,yc,zc};
356
          mTouch.add(touch);
357
          }
358
        }
359

    
360
    mNumCubitTouches = mTouch.size();
361
    mCubitTouch = new float[mNumCubitTouches][];
362
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
363

    
364
    // now sort the touches so that the order agrees with 'Andreas signature' as defined here:
365
    // https://twistypuzzles.com/forum/viewtopic.php?p=415466#p415466
366
    // i.e. we need to sort by Y first (increasing) then by Z (decreasing) then by X (decreasing)
367
    // i.e. we need to sort by 100Y-10Z-X (increasing)
368

    
369
    for(int i=0; i<mNumCubitTouches; i++)
370
      {
371
      float[] ci = mCubitTouch[i];
372
      float val_i = 100*ci[1]-10*ci[2]-ci[0];
373

    
374
      for(int j=i+1; j<mNumCubitTouches; j++)
375
        {
376
        float[] cj = mCubitTouch[j];
377
        float val_j = 100*cj[1]-10*cj[2]-cj[0];
378

    
379
        if( val_j<val_i )
380
          {
381
          mCubitTouch[i] = cj;
382
          mCubitTouch[j] = ci;
383
          val_i = val_j;
384
          ci = cj;
385
          }
386
        }
387
      }
388
    }
389

    
390
///////////////////////////////////////////////////////////////////////////////////////////////////
391

    
392
  private void prepareAllCycles()
393
    {
394
    ArrayList<float[][]> cycles0 = new ArrayList<>();
395
    ArrayList<float[][]> cycles1 = new ArrayList<>();
396
    ArrayList<float[][]> cycles2 = new ArrayList<>();
397

    
398
    mNumLeftCyclesPerLayer = new int[3];
399
    mNumCentCyclesPerLayer = new int[3];
400
    mNumInneCyclesPerLayer = new int[3];
401

    
402
    if( mLayer[1]==mLayer[2] ) generate4Cycles(cycles0,0);
403
    else                       generate2Cycles(cycles0,0);
404
    if( mLayer[0]==mLayer[2] ) generate4Cycles(cycles1,1);
405
    else                       generate2Cycles(cycles1,1);
406
    if( mLayer[0]==mLayer[1] ) generate4Cycles(cycles2,2);
407
    else                       generate2Cycles(cycles2,2);
408

    
409
    mCycles = new int[3][][][];
410

    
411
    mCycles[0] = fillUpCycles(cycles0,0,mLayer[0]);
412
    mCycles[1] = fillUpCycles(cycles1,1,mLayer[1]);
413
    mCycles[2] = fillUpCycles(cycles2,2,mLayer[2]);
414
    }
415

    
416
///////////////////////////////////////////////////////////////////////////////////////////////////
417

    
418
  private void generate4Cycles(ArrayList<float[][]> cycles, int axis)
419
    {
420
    for(int i=0; i<mNumCubitTouches; i++)
421
      {
422
      int i0 = rotateIndex(axis,i);
423
      if( i0<=i ) continue;
424
      int i1 = rotateIndex(axis,i0);
425
      if( i1<=i ) continue;
426
      int i2 = rotateIndex(axis,i1);
427
      if( i2<=i ) continue;
428

    
429
      float[] f0 = getCubitTouchOfIndex(i);
430
      float[] f1 = getCubitTouchOfIndex(i0);
431
      float[] f2 = getCubitTouchOfIndex(i1);
432
      float[] f3 = getCubitTouchOfIndex(i2);
433

    
434
      int l = (int)(2*f0[axis]+mLayer[axis]);
435

    
436
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
437
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
438
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
439

    
440
      float[][] cycle = new float[][] { f0,f1,f2,f3 };
441
      cycles.add(cycle);
442
      }
443
    }
444

    
445
///////////////////////////////////////////////////////////////////////////////////////////////////
446

    
447
  private void generate2Cycles(ArrayList<float[][]> cycles, int axis)
448
    {
449
    for(int i=0; i<mNumCubitTouches; i++)
450
      {
451
      int i0 = rotateIndex2(axis,i);
452
      if( i0<=i ) continue;
453

    
454
      float[] f0 = getCubitTouchOfIndex(i);
455
      float[] f1 = getCubitTouchOfIndex(i0);
456

    
457
      int l = (int)(2*f0[axis]+mLayer[axis]);
458

    
459
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
460
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
461
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
462

    
463
      float[][] cycle = new float[][] { f0,f1 };
464
      cycles.add(cycle);
465
      }
466
    }
467

    
468
///////////////////////////////////////////////////////////////////////////////////////////////////
469

    
470
  private int[][][] fillUpCycles(ArrayList<float[][]> cyc, int axis, int numLayers)
471
    {
472
    int numCycles = cyc.size();
473
    int[] index = new int[numLayers];
474

    
475
    int numFirst = mNumCentCyclesPerLayer[axis];
476
    int numNext  = mNumLeftCyclesPerLayer[axis] + mNumInneCyclesPerLayer[axis];
477
    int numLast  = mNumLeftCyclesPerLayer[axis] + numFirst;
478

    
479
    int[][][] ret = new int[numLayers][][];
480
    ret[          0] = new int[numFirst][];
481
    ret[numLayers-1] = new int[numLast][];
482

    
483
    for(int i=1; i<numLayers-1; i++) ret[i] = new int[numNext][];
484

    
485
    for(int i=0; i<numCycles; i++)
486
      {
487
      float[][] cycle = cyc.remove(0);
488
      int layer = (int)(cycle[0][axis]+numLayers*0.5f);
489

    
490
      if( cycle.length==4 )
491
        {
492
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
493
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
494
        int i2 = getIndexOfCubitTouch(cycle[2][0],cycle[2][1],cycle[2][2]);
495
        int i3 = getIndexOfCubitTouch(cycle[3][0],cycle[3][1],cycle[3][2]);
496
        ret[layer][index[layer]] = new int[] {i0,i1,i2,i3};
497
        index[layer]++;
498
        }
499
      else
500
        {
501
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
502
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
503
        ret[layer][index[layer]] = new int[] {i0,i1};
504
        index[layer]++;
505
        }
506
      }
507

    
508
    return ret;
509
    }
510

    
511
///////////////////////////////////////////////////////////////////////////////////////////////////
512

    
513
  private int rotateIndex(int axis, int index)
514
    {
515
    float[] touch = getCubitTouchOfIndex(index);
516

    
517
    switch(axis)
518
      {
519
      case 0: return getIndexOfCubitTouch(+touch[0],+touch[2],-touch[1]);
520
      case 1: return getIndexOfCubitTouch(-touch[2],+touch[1],+touch[0]);
521
      case 2: return getIndexOfCubitTouch(+touch[1],-touch[0],+touch[2]);
522
      }
523

    
524
    return -1;
525
    }
526

    
527
///////////////////////////////////////////////////////////////////////////////////////////////////
528

    
529
  private int rotateIndex2(int axis, int index)
530
    {
531
    float[] touch = getCubitTouchOfIndex(index);
532

    
533
    switch(axis)
534
      {
535
      case 0: return getIndexOfCubitTouch(+touch[0],-touch[1],-touch[2]);
536
      case 1: return getIndexOfCubitTouch(-touch[0],+touch[1],-touch[2]);
537
      case 2: return getIndexOfCubitTouch(-touch[0],-touch[1],+touch[2]);
538
      }
539

    
540
    return -1;
541
    }
542

    
543
///////////////////////////////////////////////////////////////////////////////////////////////////
544

    
545
  private int getIndexOfCubitTouch(float x, float y, float z)
546
    {
547
    for(int i=0; i<mNumCubitTouches; i++)
548
      {
549
      float[] touch = mCubitTouch[i];
550
      if( touch[0]==x && touch[1]==y && touch[2]==z ) return i;
551
      }
552

    
553
    return -1;
554
    }
555

    
556
///////////////////////////////////////////////////////////////////////////////////////////////////
557

    
558
  private float[] getCubitTouchOfIndex(int index)
559
    {
560
    return mCubitTouch[index];
561
    }
562

    
563
///////////////////////////////////////////////////////////////////////////////////////////////////
564

    
565
  private boolean areNeighbours(float dx, float dy, float dz)
566
    {
567
    return dx*dx+dy*dy+dz*dz<1.1f;
568
    }
569

    
570
///////////////////////////////////////////////////////////////////////////////////////////////////
571

    
572
  private long getBit(int index)
573
    {
574
    switch(index/64)
575
      {
576
      case 0: return (mSignature3>>(index%64))&0x1;
577
      case 1: return (mSignature2>>(index%64))&0x1;
578
      case 2: return (mSignature1>>(index%64))&0x1;
579
      default: android.util.Log.e("D", "error in getBit, index="+index);
580
      }
581

    
582
    return 0L;
583
    }
584

    
585
///////////////////////////////////////////////////////////////////////////////////////////////////
586

    
587
  private void setBit(int index, long bit)
588
    {
589
    long diff = (1L<<(index%64));
590

    
591
    switch(index/64)
592
      {
593
      case 0: if( bit!=0 ) mSignature3 |= diff;
594
              else         mSignature3 &=~diff;
595
              break;
596
      case 1: if( bit!=0 ) mSignature2 |= diff;
597
              else         mSignature2 &=~diff;
598
              break;
599
      case 2: if( bit!=0 ) mSignature1 |= diff;
600
              else         mSignature1 &=~diff;
601
              break;
602
      default: android.util.Log.e("D", "error in setBit, index="+index);
603
      }
604
    }
605
}
(9-9/11)