Project

General

Profile

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

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

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
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 boolean isEqual(ObjectSignature sig)
169
    {
170
    return mSignature1==sig.mSignature1 && mSignature2==sig.mSignature2 && mSignature3==sig.mSignature3;
171
    }
172

    
173
///////////////////////////////////////////////////////////////////////////////////////////////////
174

    
175
  public long getLong1()
176
    {
177
    return mSignature1;
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

    
182
  public long getLong2()
183
    {
184
    return mSignature2;
185
    }
186

    
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188

    
189
  public long getLong3()
190
    {
191
    return mSignature3;
192
    }
193

    
194
///////////////////////////////////////////////////////////////////////////////////////////////////
195

    
196
  public String getString()
197
    {
198
    String sig1 = String.format("0x%016X", mSignature1);
199
    String sig2 = String.format("0x%016X", mSignature2);
200
    String sig3 = String.format("0x%016X", mSignature3);
201

    
202
    return sig1+"_"+sig2+"_"+sig3;
203
    }
204

    
205
///////////////////////////////////////////////////////////////////////////////////////////////////
206

    
207
  public boolean isUnblockedFromLeft(int axis, int layer)
208
    {
209
    if(layer>0)
210
      for(int index=0; index<mNumCubitTouches; index++)
211
        if( getBit(index)!=0 )
212
          {
213
          float[] touch = getCubitTouchOfIndex(index);
214
          if( belongsLeft(touch,axis,layer) ) return false;
215
          }
216

    
217
    return true;
218
    }
219

    
220
///////////////////////////////////////////////////////////////////////////////////////////////////
221

    
222
  public ObjectSignature turn(int axis, int layer, int turn)
223
    {
224
    ObjectSignature ret = new ObjectSignature(this);
225

    
226
    // I don't understand it, but Firebase shows mCycles is occasionally null here.
227
    if( mCycles!=null && mCycles[axis]!=null )
228
      {
229
      int[][] cycles = mCycles[axis][layer];
230

    
231
      // it can happen that there are no cycles in this layer: 2x1x2 axis 0 layer 0.
232
      if( cycles!=null && cycles.length>0 && cycles[0]!=null )
233
        {
234
        if( cycles[0].length==4 ) for(int[] cyc : cycles) ret.cycle4(turn,cyc);
235
        else                      for(int[] cyc : cycles) ret.cycle2(cyc);
236
        }
237
      }
238

    
239
    return ret;
240
    }
241

    
242
///////////////////////////////////////////////////////////////////////////////////////////////////
243

    
244
  private boolean belongsLeft(float[] point, int axis, int layer)
245
    {
246
    return 2*point[axis]+mLayer[axis] == 2*layer;
247
    }
248

    
249
///////////////////////////////////////////////////////////////////////////////////////////////////
250

    
251
  private void cycle2(int[] cyc)
252
    {
253
    int index0 = cyc[0];
254
    int index1 = cyc[1];
255

    
256
    long b0 = getBit(index0);
257
    long b1 = getBit(index1);
258

    
259
    setBit(index1,b0);
260
    setBit(index0,b1);
261
    }
262

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264

    
265
  private void cycle4(int turn, int[] cyc)
266
    {
267
    int index0 = cyc[0];
268
    int index1 = cyc[1];
269
    int index2 = cyc[2];
270
    int index3 = cyc[3];
271

    
272
    long b0 = getBit(index0);
273
    long b1 = getBit(index1);
274
    long b2 = getBit(index2);
275
    long b3 = getBit(index3);
276

    
277
    switch(turn)
278
      {
279
      case 1: setBit(index0,b3);
280
              setBit(index1,b0);
281
              setBit(index2,b1);
282
              setBit(index3,b2);
283
              break;
284
      case 2: setBit(index0,b2);
285
              setBit(index1,b3);
286
              setBit(index2,b0);
287
              setBit(index3,b1);
288
              break;
289
      case 3: setBit(index0,b1);
290
              setBit(index1,b2);
291
              setBit(index2,b3);
292
              setBit(index3,b0);
293
              break;
294
      }
295
    }
296

    
297
///////////////////////////////////////////////////////////////////////////////////////////////////
298

    
299
  private void prepareCubitTouch()
300
    {
301
    int numCenters = mLayer[0]*mLayer[1]*mLayer[2];
302
    if( mLayer[0]>1 && mLayer[1]>1 && mLayer[2]>1 ) numCenters -= (mLayer[0]-2)*(mLayer[1]-2)*(mLayer[2]-2);
303

    
304
    float[][] centers = new float[numCenters][];
305
    int index = 0;
306

    
307
    for(int i=0; i<mLayer[0]; i++)
308
      for(int j=0; j<mLayer[1]; j++)
309
        for(int k=0; k<mLayer[2]; k++)
310
          if( (i==0) || (i==mLayer[0]-1) || (j==0) || (j==mLayer[1]-1) || (k==0) || (k==mLayer[2]-1) )
311
            {
312
            centers[index++] = new float[] { i+0.5f*(1-mLayer[0]), j+0.5f*(1-mLayer[1]), k+0.5f*(1-mLayer[2]) };
313
            }
314

    
315
    ArrayList<float[]> mTouch = new ArrayList<>();
316

    
317
    for(int i=0; i<numCenters; i++)
318
      for(int j=i+1; j<numCenters; j++)
319
        {
320
        float[] c0 = centers[i];
321
        float[] c1 = centers[j];
322

    
323
        float x1 = c0[0];
324
        float y1 = c0[1];
325
        float z1 = c0[2];
326
        float x2 = c1[0];
327
        float y2 = c1[1];
328
        float z2 = c1[2];
329

    
330
        if( areNeighbours(x1-x2,y1-y2,z1-z2) )
331
          {
332
          float xc = (x1+x2)/2;
333
          float yc = (y1+y2)/2;
334
          float zc = (z1+z2)/2;
335

    
336
          float[] touch = new float[] {xc,yc,zc};
337
          mTouch.add(touch);
338
          }
339
        }
340

    
341
    mNumCubitTouches = mTouch.size();
342
    mCubitTouch = new float[mNumCubitTouches][];
343
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
344

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

    
350
    for(int i=0; i<mNumCubitTouches; i++)
351
      {
352
      float[] ci = mCubitTouch[i];
353
      float val_i = 100*ci[1]-10*ci[2]-ci[0];
354

    
355
      for(int j=i+1; j<mNumCubitTouches; j++)
356
        {
357
        float[] cj = mCubitTouch[j];
358
        float val_j = 100*cj[1]-10*cj[2]-cj[0];
359

    
360
        if( val_j<val_i )
361
          {
362
          mCubitTouch[i] = cj;
363
          mCubitTouch[j] = ci;
364
          val_i = val_j;
365
          ci = cj;
366
          }
367
        }
368
      }
369
    }
370

    
371
///////////////////////////////////////////////////////////////////////////////////////////////////
372

    
373
  private void prepareAllCycles()
374
    {
375
    ArrayList<float[][]> cycles0 = new ArrayList<>();
376
    ArrayList<float[][]> cycles1 = new ArrayList<>();
377
    ArrayList<float[][]> cycles2 = new ArrayList<>();
378

    
379
    mNumLeftCyclesPerLayer = new int[3];
380
    mNumCentCyclesPerLayer = new int[3];
381
    mNumInneCyclesPerLayer = new int[3];
382

    
383
    if( mLayer[1]==mLayer[2] ) generate4Cycles(cycles0,0);
384
    else                       generate2Cycles(cycles0,0);
385
    if( mLayer[0]==mLayer[2] ) generate4Cycles(cycles1,1);
386
    else                       generate2Cycles(cycles1,1);
387
    if( mLayer[0]==mLayer[1] ) generate4Cycles(cycles2,2);
388
    else                       generate2Cycles(cycles2,2);
389

    
390
    mCycles = new int[3][][][];
391

    
392
    mCycles[0] = fillUpCycles(cycles0,0,mLayer[0]);
393
    mCycles[1] = fillUpCycles(cycles1,1,mLayer[1]);
394
    mCycles[2] = fillUpCycles(cycles2,2,mLayer[2]);
395
    }
396

    
397
///////////////////////////////////////////////////////////////////////////////////////////////////
398

    
399
  private void generate4Cycles(ArrayList<float[][]> cycles, int axis)
400
    {
401
    for(int i=0; i<mNumCubitTouches; i++)
402
      {
403
      int i0 = rotateIndex(axis,i);
404
      if( i0<=i ) continue;
405
      int i1 = rotateIndex(axis,i0);
406
      if( i1<=i ) continue;
407
      int i2 = rotateIndex(axis,i1);
408
      if( i2<=i ) continue;
409

    
410
      float[] f0 = getCubitTouchOfIndex(i);
411
      float[] f1 = getCubitTouchOfIndex(i0);
412
      float[] f2 = getCubitTouchOfIndex(i1);
413
      float[] f3 = getCubitTouchOfIndex(i2);
414

    
415
      int l = (int)(2*f0[axis]+mLayer[axis]);
416

    
417
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
418
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
419
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
420

    
421
      float[][] cycle = new float[][] { f0,f1,f2,f3 };
422
      cycles.add(cycle);
423
      }
424
    }
425

    
426
///////////////////////////////////////////////////////////////////////////////////////////////////
427

    
428
  private void generate2Cycles(ArrayList<float[][]> cycles, int axis)
429
    {
430
    for(int i=0; i<mNumCubitTouches; i++)
431
      {
432
      int i0 = rotateIndex2(axis,i);
433
      if( i0<=i ) continue;
434

    
435
      float[] f0 = getCubitTouchOfIndex(i);
436
      float[] f1 = getCubitTouchOfIndex(i0);
437

    
438
      int l = (int)(2*f0[axis]+mLayer[axis]);
439

    
440
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
441
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
442
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
443

    
444
      float[][] cycle = new float[][] { f0,f1 };
445
      cycles.add(cycle);
446
      }
447
    }
448

    
449
///////////////////////////////////////////////////////////////////////////////////////////////////
450

    
451
  private int[][][] fillUpCycles(ArrayList<float[][]> cyc, int axis, int numLayers)
452
    {
453
    int numCycles = cyc.size();
454
    int[] index = new int[numLayers];
455

    
456
    int numFirst = mNumCentCyclesPerLayer[axis];
457
    int numNext  = mNumLeftCyclesPerLayer[axis] + mNumInneCyclesPerLayer[axis];
458
    int numLast  = mNumLeftCyclesPerLayer[axis] + numFirst;
459

    
460
    int[][][] ret = new int[numLayers][][];
461
    ret[          0] = new int[numFirst][];
462
    ret[numLayers-1] = new int[numLast][];
463

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

    
466
    for(int i=0; i<numCycles; i++)
467
      {
468
      float[][] cycle = cyc.remove(0);
469
      int layer = (int)(cycle[0][axis]+numLayers*0.5f);
470

    
471
      if( cycle.length==4 )
472
        {
473
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
474
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
475
        int i2 = getIndexOfCubitTouch(cycle[2][0],cycle[2][1],cycle[2][2]);
476
        int i3 = getIndexOfCubitTouch(cycle[3][0],cycle[3][1],cycle[3][2]);
477
        ret[layer][index[layer]] = new int[] {i0,i1,i2,i3};
478
        index[layer]++;
479
        }
480
      else
481
        {
482
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
483
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
484
        ret[layer][index[layer]] = new int[] {i0,i1};
485
        index[layer]++;
486
        }
487
      }
488

    
489
    return ret;
490
    }
491

    
492
///////////////////////////////////////////////////////////////////////////////////////////////////
493

    
494
  private int rotateIndex(int axis, int index)
495
    {
496
    float[] touch = getCubitTouchOfIndex(index);
497

    
498
    switch(axis)
499
      {
500
      case 0: return getIndexOfCubitTouch(+touch[0],+touch[2],-touch[1]);
501
      case 1: return getIndexOfCubitTouch(-touch[2],+touch[1],+touch[0]);
502
      case 2: return getIndexOfCubitTouch(+touch[1],-touch[0],+touch[2]);
503
      }
504

    
505
    return -1;
506
    }
507

    
508
///////////////////////////////////////////////////////////////////////////////////////////////////
509

    
510
  private int rotateIndex2(int axis, int index)
511
    {
512
    float[] touch = getCubitTouchOfIndex(index);
513

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

    
521
    return -1;
522
    }
523

    
524
///////////////////////////////////////////////////////////////////////////////////////////////////
525

    
526
  private int getIndexOfCubitTouch(float x, float y, float z)
527
    {
528
    for(int i=0; i<mNumCubitTouches; i++)
529
      {
530
      float[] touch = mCubitTouch[i];
531
      if( touch[0]==x && touch[1]==y && touch[2]==z ) return i;
532
      }
533

    
534
    return -1;
535
    }
536

    
537
///////////////////////////////////////////////////////////////////////////////////////////////////
538

    
539
  private float[] getCubitTouchOfIndex(int index)
540
    {
541
    return mCubitTouch[index];
542
    }
543

    
544
///////////////////////////////////////////////////////////////////////////////////////////////////
545

    
546
  private boolean areNeighbours(float dx, float dy, float dz)
547
    {
548
    return dx*dx+dy*dy+dz*dz<1.1f;
549
    }
550

    
551
///////////////////////////////////////////////////////////////////////////////////////////////////
552

    
553
  private long getBit(int index)
554
    {
555
    switch(index/64)
556
      {
557
      case 0: return (mSignature3>>(index%64))&0x1;
558
      case 1: return (mSignature2>>(index%64))&0x1;
559
      case 2: return (mSignature1>>(index%64))&0x1;
560
      default: android.util.Log.e("D", "error in getBit, index="+index);
561
      }
562

    
563
    return 0L;
564
    }
565

    
566
///////////////////////////////////////////////////////////////////////////////////////////////////
567

    
568
  private void setBit(int index, long bit)
569
    {
570
    long diff = (1L<<(index%64));
571

    
572
    switch(index/64)
573
      {
574
      case 0: if( bit!=0 ) mSignature3 |= diff;
575
              else         mSignature3 &=~diff;
576
              break;
577
      case 1: if( bit!=0 ) mSignature2 |= diff;
578
              else         mSignature2 &=~diff;
579
              break;
580
      case 2: if( bit!=0 ) mSignature1 |= diff;
581
              else         mSignature1 &=~diff;
582
              break;
583
      default: android.util.Log.e("D", "error in setBit, index="+index);
584
      }
585
    }
586
}
(9-9/11)