Project

General

Profile

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

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

1 00947987 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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 3103c3c8 Leszek Koltunski
import org.distorted.objectlib.helpers.OperatingSystemInterface;
13 00947987 Leszek Koltunski
14 884b702b Leszek Koltunski
import java.io.ByteArrayOutputStream;
15
import java.io.IOException;
16
import java.io.InputStream;
17 00947987 Leszek Koltunski
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19
20 f079bf77 Leszek Koltunski
public abstract class TablebasesPruning extends TablebasesAbstract
21 884b702b Leszek Koltunski
{
22 bdcb662f Leszek Koltunski
  private final int mGodsNumber;
23
24 f079bf77 Leszek Koltunski
  private PruningTable[] mHighTables;
25
  private PruningTable[] mMidTables;
26 3feba94e Leszek Koltunski
  private int mLowestHigh, mHighestMid, mLowestMid;
27 00947987 Leszek Koltunski
28 15d1f6ad Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
29
30 f079bf77 Leszek Koltunski
  abstract int[] getMidPruningLevels();
31
  abstract int[] getHighPruningLevels();
32
  abstract int getGodsNumber();
33 bdcb662f Leszek Koltunski
  abstract boolean moveCanProceed(int lastA, int lastL, int currA, int currL);
34 15d1f6ad Leszek Koltunski
35 00947987 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
36
37 3103c3c8 Leszek Koltunski
  private PruningTable createPruningTable(OperatingSystemInterface os, int id)
38 00947987 Leszek Koltunski
    {
39 3103c3c8 Leszek Koltunski
    InputStream stream = os.openLocalFile(id);
40 884b702b Leszek Koltunski
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
41 00947987 Leszek Koltunski
42 884b702b Leszek Koltunski
    int nRead;
43
    byte[] tmp = new byte[16384];
44 00947987 Leszek Koltunski
45 884b702b Leszek Koltunski
    try
46 00947987 Leszek Koltunski
      {
47 884b702b Leszek Koltunski
      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
48 00947987 Leszek Koltunski
        {
49 884b702b Leszek Koltunski
        buffer.write(tmp, 0, nRead);
50 00947987 Leszek Koltunski
        }
51 884b702b Leszek Koltunski
      stream.close();
52
      byte[] data = buffer.toByteArray();
53
      buffer.close();
54 f079bf77 Leszek Koltunski
      return new PruningTable(data);
55 884b702b Leszek Koltunski
      }
56
    catch(IOException ex)
57 00947987 Leszek Koltunski
      {
58 884b702b Leszek Koltunski
      mInitialized = false;
59 f079bf77 Leszek Koltunski
      return null;
60 00947987 Leszek Koltunski
      }
61 884b702b Leszek Koltunski
    }
62 00947987 Leszek Koltunski
63 884b702b Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
64 00947987 Leszek Koltunski
65 f079bf77 Leszek Koltunski
  public TablebasesPruning()
66 884b702b Leszek Koltunski
    {
67
    super();
68 f079bf77 Leszek Koltunski
    mGodsNumber = getGodsNumber();
69 7363eaa6 Leszek Koltunski
    mInitialized = false;
70 00947987 Leszek Koltunski
    }
71
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73
74 3103c3c8 Leszek Koltunski
  public TablebasesPruning(OperatingSystemInterface os, int[] midIDs, int[] highIDs)
75 00947987 Leszek Koltunski
    {
76 884b702b Leszek Koltunski
    super();
77
78 f079bf77 Leszek Koltunski
    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 884b702b Leszek Koltunski
    mInitialized = true;
85
86 3103c3c8 Leszek Koltunski
    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 bdcb662f Leszek Koltunski
89
    computeLowHigh();
90 00947987 Leszek Koltunski
    }
91
92 15d1f6ad Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
93
94
  public byte[][] getPacked()
95
    {
96 7363eaa6 Leszek Koltunski
    if( !mInitialized )
97
      {
98 f079bf77 Leszek Koltunski
      int[] midLevels = getMidPruningLevels();
99
      int numMidLevels= midLevels!=null ? midLevels.length : 0;
100
      int[] highLevels = getHighPruningLevels();
101
      int numHighLevels= highLevels!=null ? highLevels.length : 0;
102 7363eaa6 Leszek Koltunski
      int maxLevel = 0;
103 15d1f6ad Leszek Koltunski
104 f079bf77 Leszek Koltunski
      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 15d1f6ad Leszek Koltunski
116 7363eaa6 Leszek Koltunski
      createTablebase(maxLevel);
117 f079bf77 Leszek Koltunski
      mMidTables = numMidLevels >0 ? new PruningTable[numMidLevels ] : null;
118
      mHighTables= numHighLevels>0 ? new PruningTable[numHighLevels] : null;
119 15d1f6ad Leszek Koltunski
120 f079bf77 Leszek Koltunski
      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 15d1f6ad Leszek Koltunski
125 bdcb662f Leszek Koltunski
      computeLowHigh();
126
127 7363eaa6 Leszek Koltunski
      mInitialized = true;
128 15d1f6ad Leszek Koltunski
      }
129
130 f079bf77 Leszek Koltunski
    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 7363eaa6 Leszek Koltunski
137 15d1f6ad Leszek Koltunski
    return data;
138
    }
139
140 00947987 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
141
142 bdcb662f Leszek Koltunski
  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 693cc52e Leszek Koltunski
    for(int cubit=0; cubit<mNumCubits; cubit++) mRotRow[cubit] = computeRow(cubit,quats[cubit]);
178 bdcb662f Leszek Koltunski
179
    for(int s=0; s<mScalingFactor; s++)
180
      {
181
      int ax    = move[0];
182
      int layer = move[1];
183
      int quat  = move[3];
184
185
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
186
        {
187 3feba94e Leszek Koltunski
        int bitLayer = computeBitLayer(ax,layer);
188 bdcb662f Leszek Koltunski
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
189
190
        for(int cubit=0; cubit<mNumCubits; cubit++)
191 3feba94e Leszek Koltunski
          if( (mRotRow[cubit][ax] & bitLayer) != 0 )
192 bdcb662f Leszek Koltunski
            {
193
            int currQuat = tmpQuats[cubit];
194
            int newQuat = getMultQuat(quat,currQuat);
195
            tmpQuats[cubit] = newQuat;
196
            }
197
198
        int childIndex = getIndex(tmpQuats);
199
200
        if( tableIndex>0 )
201
          {
202
          if( tables[tableIndex-1].contains(childIndex) ) return childIndex;
203
          }
204
        else if( !tables[0].contains(childIndex) )
205
          {
206
          if( tables.length<=1 || !tables[1].contains(childIndex) ) return childIndex;
207
          }
208
        }
209
210
      getNextAxisLayerAngleQuat(move);
211
      }
212
213
    return -1;
214
    }
215
216
///////////////////////////////////////////////////////////////////////////////////////////////////
217
218
  private int traversePruningBlock(int[][] moves, PruningTable[] tables, int tableIndex, int index, int lastA, int lastR)
219 00947987 Leszek Koltunski
    {
220 bdcb662f Leszek Koltunski
    int movesIndex = 1;
221 aa0757b6 Leszek Koltunski
    int[] move, tmpQuats = new int[mNumCubits];
222 bdcb662f Leszek Koltunski
223
    do
224
      {
225 aa0757b6 Leszek Koltunski
      move  = moves[movesIndex++];
226 bdcb662f Leszek Koltunski
      index = traverseDownTable(index,tables,tableIndex,lastA,lastR,move,tmpQuats);
227
      lastA = move[0];
228
      lastR = move[1];
229
      tableIndex--;
230
      }
231
    while( tableIndex>=0 );
232
233
    return index;
234
    }
235
236
///////////////////////////////////////////////////////////////////////////////////////////////////
237
// ret: [0][] --> (new index,new depth,?,?) ; [1...N-1][] --> moves. (or null if index not in High)
238
239
  private int[][] traverseBlock(int index, PruningTable[] tables, int lastA, int lastR)
240
    {
241
    int num = (tables==null) ? 0 : tables.length;
242
    int tableIndex = -1;
243
244
    for( int i=0; i<num; i++ )
245
      if( tables[i].contains(index) )
246
        {
247
        tableIndex = i;
248
        break;
249
        }
250
251 b9633e5f Leszek Koltunski
    if( tableIndex<0 ) return null;
252 bdcb662f Leszek Koltunski
253
    int[][] ret = new int[tableIndex+2][4];
254
    ret[0][1] = tables[0].getLevel()-1;
255
    ret[0][0] = traversePruningBlock(ret,tables,tableIndex,index,lastA, lastR);
256
257
    return ret;
258
    }
259
260
///////////////////////////////////////////////////////////////////////////////////////////////////
261
262
  private int midTablesContain(int index)
263
    {
264
    int num = mMidTables.length;
265
266
    for( int i=0; i<num; i++ )
267
      if( mMidTables[i].contains(index) )
268
        return i;
269
270
    return -1;
271
    }
272
273
///////////////////////////////////////////////////////////////////////////////////////////////////
274
275 f51c164f Leszek Koltunski
  private boolean jumpMidSolvedRecursive(int[] quats, int jump, int depth, int lastA, int lastR,
276
                                         int[][] solution, int[][] tmpE, int[][][] rotRowE )
277 bdcb662f Leszek Koltunski
    {
278 aa0757b6 Leszek Koltunski
    int numQuats  = quats.length;
279
    int[] move    = solution[depth];
280 f51c164f Leszek Koltunski
    int[] tmp     = tmpE[jump];
281
    int[][] rotRow= rotRowE[jump];
282 bdcb662f Leszek Koltunski
283
    move[0]=0;
284
    move[1]=0;
285
    move[2]=1;
286
    move[3]=1;
287
288 693cc52e Leszek Koltunski
    for(int cubit=0; cubit<mNumCubits; cubit++) rotRow[cubit] = computeRow(cubit,quats[cubit]);
289 bdcb662f Leszek Koltunski
290
    for(int s=0; s<mScalingFactor; s++)
291
      {
292
      int ax    = move[0];
293
      int layer = move[1];
294
      int quat  = move[3];
295
296
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,ax,layer) )
297
        {
298 3feba94e Leszek Koltunski
        int bitLayer = computeBitLayer(ax,layer);
299 bdcb662f Leszek Koltunski
        System.arraycopy(quats, 0, tmp, 0, numQuats);
300
301
        for(int cubit=0; cubit<mNumCubits; cubit++)
302 3feba94e Leszek Koltunski
          if( (rotRow[cubit][ax] & bitLayer) != 0 )
303 693cc52e Leszek Koltunski
            tmp[cubit] = getMultQuat(quat,tmp[cubit]);
304 bdcb662f Leszek Koltunski
305
        int childIndex = getIndex(tmp);
306
307 5b9f1cec Leszek Koltunski
        if( isSolved(childIndex) )
308 bdcb662f Leszek Koltunski
          {
309
          solution[0][0] = childIndex;
310
          solution[0][1] = 0;
311
          return true;
312
          }
313
314
        int containingTable = midTablesContain(childIndex);
315
316
        if( containingTable>=0 )
317
          {
318
          solution[0][0] = childIndex;
319
          solution[0][1] = mMidTables[containingTable].getLevel();
320
          return true;
321
          }
322
323 f51c164f Leszek Koltunski
        if( jump>1 && jumpMidSolvedRecursive(tmp, jump-1, depth+1, ax, layer, solution, tmpE, rotRowE) )
324 bdcb662f Leszek Koltunski
          {
325
          return true;
326
          }
327
        }
328
329
      getNextAxisLayerAngleQuat(move);
330
      }
331
332
    return false;
333
    }
334
335
///////////////////////////////////////////////////////////////////////////////////////////////////
336 cd595931 Leszek Koltunski
// ret: [0][] --> (new index,new depth,num moves,?) ; [1...N-1][] --> moves.
337 bdcb662f Leszek Koltunski
338 5b9d6595 Leszek Koltunski
  private int[][] jumpToMidOrSolved(int index, int maxJump, int lastA, int lastR)
339 bdcb662f Leszek Koltunski
    {
340 aa0757b6 Leszek Koltunski
    if( midTablesContain(index)>=0 ) return null;
341
342 bdcb662f Leszek Koltunski
    int[][] solution = new int[maxJump+1][4];
343 ca2ba7a1 Leszek Koltunski
    int[] quats = getQuats(index);
344 bdcb662f Leszek Koltunski
345 f51c164f Leszek Koltunski
    int[][] tmpE = new int[maxJump+1][mNumCubits];
346
    int[][][] rotRowE = new int[maxJump+1][mNumCubits][mNumAxis];
347
348 b9633e5f Leszek Koltunski
    for(int i=1; i<=maxJump; i++)
349 f51c164f Leszek Koltunski
      if( jumpMidSolvedRecursive(quats,i,1,lastA,lastR,solution,tmpE,rotRowE) )
350 aa0757b6 Leszek Koltunski
        {
351 cd595931 Leszek Koltunski
        solution[0][2] = i;
352 bdcb662f Leszek Koltunski
        return solution;
353 aa0757b6 Leszek Koltunski
        }
354 bdcb662f Leszek Koltunski
355
    return null;
356
    }
357
358
///////////////////////////////////////////////////////////////////////////////////////////////////
359
360 ca2ba7a1 Leszek Koltunski
  private boolean jumpToSolvedRecursive(int[] quats, int jump, int depth, int lastA, int lastR, int[][] solution)
361 bdcb662f Leszek Koltunski
    {
362
    int numQuats = quats.length;
363
    int[] move   = solution[depth];
364
    int[] tmp    = new int[mNumCubits];
365 aa0757b6 Leszek Koltunski
    int[][]rotRow= new int[mNumCubits][mNumAxis];
366 bdcb662f Leszek Koltunski
367
    move[0]=0;
368
    move[1]=0;
369
    move[2]=1;
370
    move[3]=1;
371
372 693cc52e Leszek Koltunski
    for(int cubit=0; cubit<mNumCubits; cubit++) rotRow[cubit] = computeRow(cubit,quats[cubit]);
373 bdcb662f Leszek Koltunski
374
    for(int s=0; s<mScalingFactor; s++)
375
      {
376
      int ax    = move[0];
377
      int layer = move[1];
378
      int quat  = move[3];
379
380
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
381
        {
382 3feba94e Leszek Koltunski
        int bitLayer = computeBitLayer(ax,layer);
383 bdcb662f Leszek Koltunski
        System.arraycopy(quats, 0, tmp, 0, numQuats);
384
385
        for(int cubit=0; cubit<mNumCubits; cubit++)
386 3feba94e Leszek Koltunski
          if( (rotRow[cubit][ax] & bitLayer) != 0 )
387 bdcb662f Leszek Koltunski
            {
388
            int currQuat = tmp[cubit];
389
            int newQuat = getMultQuat(quat,currQuat);
390
            tmp[cubit] = newQuat;
391
            }
392
393
        int childIndex = getIndex(tmp);
394 5b9d6595 Leszek Koltunski
        if( isSolved(childIndex) ) return true;
395 ca2ba7a1 Leszek Koltunski
        if( jump>1 && jumpToSolvedRecursive(tmp, jump-1, depth+1, ax, layer, solution) ) return true;
396 bdcb662f Leszek Koltunski
        }
397
398
      getNextAxisLayerAngleQuat(move);
399
      }
400
401
    return false;
402
    }
403
404
///////////////////////////////////////////////////////////////////////////////////////////////////
405 aa0757b6 Leszek Koltunski
// ret: [0][] --> (numMoves,old depth,?,?) ; [1...N-1][] --> moves.
406 bdcb662f Leszek Koltunski
407 5b9d6595 Leszek Koltunski
  private int[][] jumpToSolved(int index, int maxJump, int lastA, int lastR)
408 bdcb662f Leszek Koltunski
    {
409
    int[][] solution = new int[maxJump+1][4];
410 ca2ba7a1 Leszek Koltunski
    int[] quats = getQuats(index);
411 bdcb662f Leszek Koltunski
412 b9633e5f Leszek Koltunski
    for(int i=1; i<=maxJump; i++)
413 ca2ba7a1 Leszek Koltunski
      if( jumpToSolvedRecursive(quats,i,1,lastA,lastR,solution) )
414 aa0757b6 Leszek Koltunski
        {
415
        solution[0][0] = i;
416 bdcb662f Leszek Koltunski
        return solution;
417 aa0757b6 Leszek Koltunski
        }
418 bdcb662f Leszek Koltunski
419 00947987 Leszek Koltunski
    return null;
420
    }
421 bdcb662f Leszek Koltunski
422
///////////////////////////////////////////////////////////////////////////////////////////////////
423
424
  private int[][] concatenateMoves(int[][] high, int[][] jump1, int[][] mid, int[][] jump2)
425
    {
426
    int len1 = high ==null ? 0 : high.length-1;
427 cd595931 Leszek Koltunski
    int len2 = jump1==null ? 0 : jump1[0][2];
428 bdcb662f Leszek Koltunski
    int len3 = mid  ==null ? 0 : mid.length-1;
429 aa0757b6 Leszek Koltunski
    int len4 = jump2==null ? 0 : jump2[0][0];
430 bdcb662f Leszek Koltunski
431
    int[][] moves = new int[len1+len2+len3+len4][];
432
    int index = 0;
433
434
    for(int i=0; i<len1; i++) moves[index++] =  high[i+1];
435
    for(int i=0; i<len2; i++) moves[index++] = jump1[i+1];
436
    for(int i=0; i<len3; i++) moves[index++] =   mid[i+1];
437
    for(int i=0; i<len4; i++) moves[index++] = jump2[i+1];
438
439
    convertMoves(moves);
440
441
    return moves;
442
    }
443
444
///////////////////////////////////////////////////////////////////////////////////////////////////
445
446 00e83f23 Leszek Koltunski
  public int[][] solution(int index, int[] extra, OperatingSystemInterface osi)
447 bdcb662f Leszek Koltunski
    {
448 295f5302 Leszek Koltunski
    if( isSolved(index) ) return null;
449 bdcb662f Leszek Koltunski
    int lastA=-1, lastR=0;
450
    int[][] highMoves = traverseBlock(index,mHighTables,lastA,lastR);
451
452
    if( highMoves!=null )
453
      {
454
      index = highMoves[0][0];
455
      int len = highMoves.length;
456
      lastA = highMoves[len-1][0];
457
      lastR = highMoves[len-1][1];
458
      }
459
460
    int maxJump = Math.max(mLowestHigh-1-mHighestMid,mLowestMid/2);
461 5b9d6595 Leszek Koltunski
    int[][] jump1Moves = jumpToMidOrSolved(index,maxJump,lastA,lastR);
462 bdcb662f Leszek Koltunski
463
    if( jump1Moves!=null )
464
      {
465 5b9d6595 Leszek Koltunski
      if( isSolved(jump1Moves[0][0]) )
466 bdcb662f Leszek Koltunski
        {
467 cd595931 Leszek Koltunski
        return concatenateMoves(null,jump1Moves,null,null);
468 bdcb662f Leszek Koltunski
        }
469
      if( jump1Moves[0][1]==mLowestMid )
470
        {
471 5b9d6595 Leszek Koltunski
        int[][] jump2Moves = jumpToSolved(index,mLowestMid-1,-1,0);
472 00e83f23 Leszek Koltunski
        if( jump2Moves==null )
473
          {
474 b2eb9a1d Leszek Koltunski
          osi.reportError("1 error jumping to Solved: "+index);
475 00e83f23 Leszek Koltunski
          return null;
476
          }
477 bdcb662f Leszek Koltunski
        return concatenateMoves(null,null,null,jump2Moves);
478
        }
479
480
      index = jump1Moves[0][0];
481 cd595931 Leszek Koltunski
      int len = jump1Moves[0][2];
482
      lastA = jump1Moves[len][0];
483
      lastR = jump1Moves[len][1];
484 bdcb662f Leszek Koltunski
      }
485
486
    int[][] midMoves = traverseBlock(index,mMidTables,lastA,lastR);
487
    if( midMoves!=null )
488
      {
489
      index = midMoves[0][0];
490
      int len = midMoves.length;
491
      lastA = midMoves[len-1][0];
492
      lastR = midMoves[len-1][1];
493
      }
494 00e83f23 Leszek Koltunski
    else
495
      {
496 20d7d940 Leszek Koltunski
      osi.reportError("error traversing mid Tables: "+index);
497 00e83f23 Leszek Koltunski
      return null;
498
      }
499 5b9d6595 Leszek Koltunski
    int[][] jump2Moves = jumpToSolved(index,mLowestMid-1,lastA,lastR);
500 00e83f23 Leszek Koltunski
    if( jump2Moves==null )
501
      {
502 20d7d940 Leszek Koltunski
      osi.reportError("2 error jumping to Solved: "+index);
503 00e83f23 Leszek Koltunski
      return null;
504
      }
505 bdcb662f Leszek Koltunski
    return concatenateMoves(highMoves,jump1Moves,midMoves,jump2Moves);
506
    }
507 00947987 Leszek Koltunski
}