Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / tablebases / TablebasesPruning.java @ f51c164f

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2023 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.objectlib.tablebases;
11

    
12
import org.distorted.objectlib.helpers.OperatingSystemInterface;
13

    
14
import java.io.ByteArrayOutputStream;
15
import java.io.IOException;
16
import java.io.InputStream;
17

    
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
public abstract class TablebasesPruning extends TablebasesAbstract
21
{
22
  private final int mGodsNumber;
23

    
24
  private PruningTable[] mHighTables;
25
  private PruningTable[] mMidTables;
26
  private int mLowestHigh, mHighestMid, mLowestMid;
27

    
28
///////////////////////////////////////////////////////////////////////////////////////////////////
29

    
30
  abstract int[] getMidPruningLevels();
31
  abstract int[] getHighPruningLevels();
32
  abstract int getGodsNumber();
33
  abstract boolean moveCanProceed(int lastA, int lastL, int currA, int currL);
34

    
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36

    
37
  private PruningTable createPruningTable(OperatingSystemInterface os, int id)
38
    {
39
    InputStream stream = os.openLocalFile(id);
40
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
41

    
42
    int nRead;
43
    byte[] tmp = new byte[16384];
44

    
45
    try
46
      {
47
      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
48
        {
49
        buffer.write(tmp, 0, nRead);
50
        }
51
      stream.close();
52
      byte[] data = buffer.toByteArray();
53
      buffer.close();
54
      return new PruningTable(data);
55
      }
56
    catch(IOException ex)
57
      {
58
      mInitialized = false;
59
      return null;
60
      }
61
    }
62

    
63
///////////////////////////////////////////////////////////////////////////////////////////////////
64

    
65
  public TablebasesPruning()
66
    {
67
    super();
68
    mGodsNumber = getGodsNumber();
69
    mInitialized = false;
70
    }
71

    
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73

    
74
  public TablebasesPruning(OperatingSystemInterface os, int[] midIDs, int[] highIDs)
75
    {
76
    super();
77

    
78
    int mid = midIDs !=null ? midIDs.length  : 0;
79
    int high= highIDs!=null ? highIDs.length : 0;
80

    
81
    mMidTables = mid >0 ? new PruningTable[mid] : null;
82
    mHighTables= high>0 ? new PruningTable[high]: null;
83
    mGodsNumber = getGodsNumber();
84
    mInitialized = true;
85

    
86
    for(int i=0; i<mid ; i++) mMidTables[i] = createPruningTable(os,midIDs[i] );
87
    for(int i=0; i<high; i++) mHighTables[i]= createPruningTable(os,highIDs[i]);
88

    
89
    computeLowHigh();
90
    }
91

    
92
///////////////////////////////////////////////////////////////////////////////////////////////////
93

    
94
  public byte[][] getPacked()
95
    {
96
    if( !mInitialized )
97
      {
98
      int[] midLevels = getMidPruningLevels();
99
      int numMidLevels= midLevels!=null ? midLevels.length : 0;
100
      int[] highLevels = getHighPruningLevels();
101
      int numHighLevels= highLevels!=null ? highLevels.length : 0;
102
      int maxLevel = 0;
103

    
104
      if( highLevels!=null )
105
        {
106
        for( int l : highLevels )
107
          if( l>maxLevel ) maxLevel = l;
108
        }
109
      else
110
        {
111
        if( midLevels!=null )
112
          for( int l : midLevels )
113
            if( l>maxLevel ) maxLevel = l;
114
        }
115

    
116
      createTablebase(maxLevel);
117
      mMidTables = numMidLevels >0 ? new PruningTable[numMidLevels ] : null;
118
      mHighTables= numHighLevels>0 ? new PruningTable[numHighLevels] : null;
119

    
120
      for(int i=0; i<numMidLevels; i++)
121
        mMidTables[i] = new PruningTable(mTablebase,midLevels[i]);
122
      for(int i=0; i<numHighLevels; i++)
123
        mHighTables[i] = new PruningTable(mTablebase,highLevels[i]);
124

    
125
      computeLowHigh();
126

    
127
      mInitialized = true;
128
      }
129

    
130
    int midNum  = mMidTables !=null ? mMidTables.length  : 0;
131
    int highNum = mHighTables!=null ? mHighTables.length : 0;
132
    byte[][] data = new byte[midNum+highNum][];
133

    
134
    for(int i=0; i<midNum ; i++) data[i       ] = mMidTables[i].getPacked();
135
    for(int i=0; i<highNum; i++) data[i+midNum] = mHighTables[i].getPacked();
136

    
137
    return data;
138
    }
139

    
140
///////////////////////////////////////////////////////////////////////////////////////////////////
141

    
142
  private void computeLowHigh()
143
    {
144
    mLowestHigh = mGodsNumber+1;
145
    int numHigh = mHighTables==null ? 0 : mHighTables.length;
146

    
147
    for( int i=0; i<numHigh; i++ )
148
      {
149
      int level = mHighTables[i].getLevel();
150
      if( i==0 || level<mLowestHigh ) mLowestHigh=level;
151
      }
152

    
153
    mLowestMid = 0;
154
    mHighestMid= 0;
155
    int numMid = mMidTables==null ? 0 : mMidTables.length;
156

    
157
    for( int i=0; i<numMid; i++ )
158
      {
159
      int level = mMidTables[i].getLevel();
160
      if( i==0 || level<mLowestMid  ) mLowestMid =level;
161
      if( i==0 || level>mHighestMid ) mHighestMid=level;
162
      }
163
    }
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

    
167
  public int traverseDownTable(int index, PruningTable[] tables, int tableIndex, int lastA, int lastR, int[] move, int[] tmpQuats)
168
    {
169
    int[] quats = getQuats(index);
170
    int numQuats = quats.length;
171

    
172
    move[0]=0;
173
    move[1]=0;
174
    move[2]=1;
175
    move[3]=1;
176

    
177
    for(int ax=0; ax<mNumAxis; ax++)
178
      for(int cubit=0; cubit<mNumCubits; cubit++)
179
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
180

    
181
    for(int s=0; s<mScalingFactor; s++)
182
      {
183
      int ax    = move[0];
184
      int layer = move[1];
185
      int quat  = move[3];
186

    
187
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
188
        {
189
        int bitLayer = computeBitLayer(ax,layer);
190
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
191

    
192
        for(int cubit=0; cubit<mNumCubits; cubit++)
193
          if( (mRotRow[cubit][ax] & bitLayer) != 0 )
194
            {
195
            int currQuat = tmpQuats[cubit];
196
            int newQuat = getMultQuat(quat,currQuat);
197
            tmpQuats[cubit] = newQuat;
198
            }
199

    
200
        int childIndex = getIndex(tmpQuats);
201

    
202
        if( tableIndex>0 )
203
          {
204
          if( tables[tableIndex-1].contains(childIndex) ) return childIndex;
205
          }
206
        else if( !tables[0].contains(childIndex) )
207
          {
208
          if( tables.length<=1 || !tables[1].contains(childIndex) ) return childIndex;
209
          }
210
        }
211

    
212
      getNextAxisLayerAngleQuat(move);
213
      }
214

    
215
    return -1;
216
    }
217

    
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219

    
220
  private int traversePruningBlock(int[][] moves, PruningTable[] tables, int tableIndex, int index, int lastA, int lastR)
221
    {
222
    int movesIndex = 1;
223
    int[] move, tmpQuats = new int[mNumCubits];
224

    
225
    do
226
      {
227
      move  = moves[movesIndex++];
228
      index = traverseDownTable(index,tables,tableIndex,lastA,lastR,move,tmpQuats);
229
      lastA = move[0];
230
      lastR = move[1];
231
      tableIndex--;
232
      }
233
    while( tableIndex>=0 );
234

    
235
    return index;
236
    }
237

    
238
///////////////////////////////////////////////////////////////////////////////////////////////////
239
// ret: [0][] --> (new index,new depth,?,?) ; [1...N-1][] --> moves. (or null if index not in High)
240

    
241
  private int[][] traverseBlock(int index, PruningTable[] tables, int lastA, int lastR)
242
    {
243
    int num = (tables==null) ? 0 : tables.length;
244
    int tableIndex = -1;
245

    
246
    for( int i=0; i<num; i++ )
247
      if( tables[i].contains(index) )
248
        {
249
        tableIndex = i;
250
        break;
251
        }
252

    
253
    if( tableIndex<0 ) return null;
254

    
255
    int[][] ret = new int[tableIndex+2][4];
256
    ret[0][1] = tables[0].getLevel()-1;
257
    ret[0][0] = traversePruningBlock(ret,tables,tableIndex,index,lastA, lastR);
258

    
259
    return ret;
260
    }
261

    
262
///////////////////////////////////////////////////////////////////////////////////////////////////
263

    
264
  private int midTablesContain(int index)
265
    {
266
    int num = mMidTables.length;
267

    
268
    for( int i=0; i<num; i++ )
269
      if( mMidTables[i].contains(index) )
270
        return i;
271

    
272
    return -1;
273
    }
274

    
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276

    
277
  private boolean jumpMidSolvedRecursive(int[] quats, int jump, int depth, int lastA, int lastR,
278
                                         int[][] solution, int[][] tmpE, int[][][] rotRowE )
279
    {
280
    int numQuats  = quats.length;
281
    int[] move    = solution[depth];
282
    int[] tmp     = tmpE[jump];
283
    int[][] rotRow= rotRowE[jump];
284

    
285
    move[0]=0;
286
    move[1]=0;
287
    move[2]=1;
288
    move[3]=1;
289

    
290
    for(int ax=0; ax<mNumAxis; ax++)
291
      for(int cubit=0; cubit<mNumCubits; cubit++)
292
        rotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
293

    
294
    for(int s=0; s<mScalingFactor; s++)
295
      {
296
      int ax    = move[0];
297
      int layer = move[1];
298
      int quat  = move[3];
299

    
300
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,ax,layer) )
301
        {
302
        int bitLayer = computeBitLayer(ax,layer);
303
        System.arraycopy(quats, 0, tmp, 0, numQuats);
304

    
305
        for(int cubit=0; cubit<mNumCubits; cubit++)
306
          if( (rotRow[cubit][ax] & bitLayer) != 0 )
307
            {
308
            int currQuat = tmp[cubit];
309
            int newQuat = getMultQuat(quat,currQuat);
310
            tmp[cubit] = newQuat;
311
            }
312

    
313
        int childIndex = getIndex(tmp);
314

    
315
        if( isSolved(childIndex) )
316
          {
317
          solution[0][0] = childIndex;
318
          solution[0][1] = 0;
319
          return true;
320
          }
321

    
322
        int containingTable = midTablesContain(childIndex);
323

    
324
        if( containingTable>=0 )
325
          {
326
          solution[0][0] = childIndex;
327
          solution[0][1] = mMidTables[containingTable].getLevel();
328
          return true;
329
          }
330

    
331
        if( jump>1 && jumpMidSolvedRecursive(tmp, jump-1, depth+1, ax, layer, solution, tmpE, rotRowE) )
332
          {
333
          return true;
334
          }
335
        }
336

    
337
      getNextAxisLayerAngleQuat(move);
338
      }
339

    
340
    return false;
341
    }
342

    
343
///////////////////////////////////////////////////////////////////////////////////////////////////
344
// ret: [0][] --> (new index,new depth,num moves,?) ; [1...N-1][] --> moves.
345

    
346
  private int[][] jumpToMidOrSolved(int index, int maxJump, int lastA, int lastR)
347
    {
348
    if( midTablesContain(index)>=0 ) return null;
349

    
350
    int[][] solution = new int[maxJump+1][4];
351
    int[] quats = getQuats(index);
352

    
353
    int[][] tmpE = new int[maxJump+1][mNumCubits];
354
    int[][][] rotRowE = new int[maxJump+1][mNumCubits][mNumAxis];
355

    
356
    for(int i=1; i<=maxJump; i++)
357
      if( jumpMidSolvedRecursive(quats,i,1,lastA,lastR,solution,tmpE,rotRowE) )
358
        {
359
        solution[0][2] = i;
360
        return solution;
361
        }
362

    
363
    return null;
364
    }
365

    
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367

    
368
  private boolean jumpToSolvedRecursive(int[] quats, int jump, int depth, int lastA, int lastR, int[][] solution)
369
    {
370
    int numQuats = quats.length;
371
    int[] move   = solution[depth];
372
    int[] tmp    = new int[mNumCubits];
373
    int[][]rotRow= new int[mNumCubits][mNumAxis];
374

    
375
    move[0]=0;
376
    move[1]=0;
377
    move[2]=1;
378
    move[3]=1;
379

    
380
    for(int ax=0; ax<mNumAxis; ax++)
381
      for(int cubit=0; cubit<mNumCubits; cubit++)
382
        rotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
383

    
384
    for(int s=0; s<mScalingFactor; s++)
385
      {
386
      int ax    = move[0];
387
      int layer = move[1];
388
      int quat  = move[3];
389

    
390
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
391
        {
392
        int bitLayer = computeBitLayer(ax,layer);
393
        System.arraycopy(quats, 0, tmp, 0, numQuats);
394

    
395
        for(int cubit=0; cubit<mNumCubits; cubit++)
396
          if( (rotRow[cubit][ax] & bitLayer) != 0 )
397
            {
398
            int currQuat = tmp[cubit];
399
            int newQuat = getMultQuat(quat,currQuat);
400
            tmp[cubit] = newQuat;
401
            }
402

    
403
        int childIndex = getIndex(tmp);
404
        if( isSolved(childIndex) ) return true;
405
        if( jump>1 && jumpToSolvedRecursive(tmp, jump-1, depth+1, ax, layer, solution) ) return true;
406
        }
407

    
408
      getNextAxisLayerAngleQuat(move);
409
      }
410

    
411
    return false;
412
    }
413

    
414
///////////////////////////////////////////////////////////////////////////////////////////////////
415
// ret: [0][] --> (numMoves,old depth,?,?) ; [1...N-1][] --> moves.
416

    
417
  private int[][] jumpToSolved(int index, int maxJump, int lastA, int lastR)
418
    {
419
    int[][] solution = new int[maxJump+1][4];
420
    int[] quats = getQuats(index);
421

    
422
    for(int i=1; i<=maxJump; i++)
423
      if( jumpToSolvedRecursive(quats,i,1,lastA,lastR,solution) )
424
        {
425
        solution[0][0] = i;
426
        return solution;
427
        }
428

    
429
    return null;
430
    }
431

    
432
///////////////////////////////////////////////////////////////////////////////////////////////////
433

    
434
  private int[][] concatenateMoves(int[][] high, int[][] jump1, int[][] mid, int[][] jump2)
435
    {
436
    int len1 = high ==null ? 0 : high.length-1;
437
    int len2 = jump1==null ? 0 : jump1[0][2];
438
    int len3 = mid  ==null ? 0 : mid.length-1;
439
    int len4 = jump2==null ? 0 : jump2[0][0];
440

    
441
    int[][] moves = new int[len1+len2+len3+len4][];
442
    int index = 0;
443

    
444
    for(int i=0; i<len1; i++) moves[index++] =  high[i+1];
445
    for(int i=0; i<len2; i++) moves[index++] = jump1[i+1];
446
    for(int i=0; i<len3; i++) moves[index++] =   mid[i+1];
447
    for(int i=0; i<len4; i++) moves[index++] = jump2[i+1];
448

    
449
    convertMoves(moves);
450

    
451
    return moves;
452
    }
453

    
454
///////////////////////////////////////////////////////////////////////////////////////////////////
455

    
456
  public int[][] solution(int index, int[] extra, OperatingSystemInterface osi)
457
    {
458
    if( isSolved(index) ) return null;
459
    int lastA=-1, lastR=0;
460
    int[][] highMoves = traverseBlock(index,mHighTables,lastA,lastR);
461

    
462
    if( highMoves!=null )
463
      {
464
      index = highMoves[0][0];
465
      int len = highMoves.length;
466
      lastA = highMoves[len-1][0];
467
      lastR = highMoves[len-1][1];
468
      }
469

    
470
    int maxJump = Math.max(mLowestHigh-1-mHighestMid,mLowestMid/2);
471
    int[][] jump1Moves = jumpToMidOrSolved(index,maxJump,lastA,lastR);
472

    
473
    if( jump1Moves!=null )
474
      {
475
      if( isSolved(jump1Moves[0][0]) )
476
        {
477
        return concatenateMoves(null,jump1Moves,null,null);
478
        }
479
      if( jump1Moves[0][1]==mLowestMid )
480
        {
481
        int[][] jump2Moves = jumpToSolved(index,mLowestMid-1,-1,0);
482
        if( jump2Moves==null )
483
          {
484
          if( osi!=null ) osi.reportError("1 error jumping to Solved: "+index);
485
          return null;
486
          }
487
        return concatenateMoves(null,null,null,jump2Moves);
488
        }
489

    
490
      index = jump1Moves[0][0];
491
      int len = jump1Moves[0][2];
492
      lastA = jump1Moves[len][0];
493
      lastR = jump1Moves[len][1];
494
      }
495

    
496
    int[][] midMoves = traverseBlock(index,mMidTables,lastA,lastR);
497
    if( midMoves!=null )
498
      {
499
      index = midMoves[0][0];
500
      int len = midMoves.length;
501
      lastA = midMoves[len-1][0];
502
      lastR = midMoves[len-1][1];
503
      }
504
    else
505
      {
506
      if( osi!=null ) osi.reportError("error traversing mid Tables: "+index);
507
      return null;
508
      }
509
    int[][] jump2Moves = jumpToSolved(index,mLowestMid-1,lastA,lastR);
510
    if( jump2Moves==null )
511
      {
512
      if( osi!=null ) osi.reportError("2 error jumping to Solved: "+index);
513
      return null;
514
      }
515
    return concatenateMoves(highMoves,jump1Moves,midMoves,jump2Moves);
516
    }
517
}
(19-19/19)