Project

General

Profile

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

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

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 884b702b Leszek Koltunski
import android.content.res.Resources;
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 884b702b Leszek Koltunski
  private boolean mInitialized;
25 f079bf77 Leszek Koltunski
  private PruningTable[] mHighTables;
26
  private PruningTable[] mMidTables;
27 bdcb662f Leszek Koltunski
  private int mLowestHigh, mHighestMid, mLowestMid;
28 00947987 Leszek Koltunski
29 15d1f6ad Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
30
31 f079bf77 Leszek Koltunski
  abstract int[] getMidPruningLevels();
32
  abstract int[] getHighPruningLevels();
33
  abstract int getGodsNumber();
34 bdcb662f Leszek Koltunski
  abstract boolean moveCanProceed(int lastA, int lastL, int currA, int currL);
35 15d1f6ad Leszek Koltunski
36 00947987 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
37
38 f079bf77 Leszek Koltunski
  private PruningTable createPruningTable(Resources res, int id)
39 00947987 Leszek Koltunski
    {
40 884b702b Leszek Koltunski
    InputStream stream = res.openRawResource(id);
41
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
42 00947987 Leszek Koltunski
43 884b702b Leszek Koltunski
    int nRead;
44
    byte[] tmp = new byte[16384];
45 00947987 Leszek Koltunski
46 884b702b Leszek Koltunski
    try
47 00947987 Leszek Koltunski
      {
48 884b702b Leszek Koltunski
      while ((nRead = stream.read(tmp, 0, tmp.length)) != -1)
49 00947987 Leszek Koltunski
        {
50 884b702b Leszek Koltunski
        buffer.write(tmp, 0, nRead);
51 00947987 Leszek Koltunski
        }
52 884b702b Leszek Koltunski
      stream.close();
53
      byte[] data = buffer.toByteArray();
54
      buffer.close();
55 f079bf77 Leszek Koltunski
      return new PruningTable(data);
56 884b702b Leszek Koltunski
      }
57
    catch(IOException ex)
58 00947987 Leszek Koltunski
      {
59 884b702b Leszek Koltunski
      mInitialized = false;
60 f079bf77 Leszek Koltunski
      return null;
61 00947987 Leszek Koltunski
      }
62 884b702b Leszek Koltunski
    }
63 00947987 Leszek Koltunski
64 884b702b Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
65 00947987 Leszek Koltunski
66 f079bf77 Leszek Koltunski
  public TablebasesPruning()
67 884b702b Leszek Koltunski
    {
68
    super();
69 f079bf77 Leszek Koltunski
    mGodsNumber = getGodsNumber();
70 7363eaa6 Leszek Koltunski
    mInitialized = false;
71 00947987 Leszek Koltunski
    }
72
73
///////////////////////////////////////////////////////////////////////////////////////////////////
74
75 f079bf77 Leszek Koltunski
  public TablebasesPruning(Resources res, int[] midIDs, int[] highIDs)
76 00947987 Leszek Koltunski
    {
77 884b702b Leszek Koltunski
    super();
78
79 f079bf77 Leszek Koltunski
    int mid = midIDs !=null ? midIDs.length  : 0;
80
    int high= highIDs!=null ? highIDs.length : 0;
81
82
    mMidTables = mid >0 ? new PruningTable[mid] : null;
83
    mHighTables= high>0 ? new PruningTable[high]: null;
84
    mGodsNumber = getGodsNumber();
85 884b702b Leszek Koltunski
    mInitialized = true;
86
87 f079bf77 Leszek Koltunski
    for(int i=0; i<mid ; i++) mMidTables[i] = createPruningTable(res,midIDs[i] );
88
    for(int i=0; i<high; i++) mHighTables[i]= createPruningTable(res,highIDs[i]);
89 bdcb662f Leszek Koltunski
90
    computeLowHigh();
91 00947987 Leszek Koltunski
    }
92
93 15d1f6ad Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
94
95
  public byte[][] getPacked()
96
    {
97 7363eaa6 Leszek Koltunski
    if( !mInitialized )
98
      {
99 f079bf77 Leszek Koltunski
      int[] midLevels = getMidPruningLevels();
100
      int numMidLevels= midLevels!=null ? midLevels.length : 0;
101
      int[] highLevels = getHighPruningLevels();
102
      int numHighLevels= highLevels!=null ? highLevels.length : 0;
103 7363eaa6 Leszek Koltunski
      int maxLevel = 0;
104 15d1f6ad Leszek Koltunski
105 f079bf77 Leszek Koltunski
      if( highLevels!=null )
106
        {
107
        for( int l : highLevels )
108
          if( l>maxLevel ) maxLevel = l;
109
        }
110
      else
111
        {
112
        if( midLevels!=null )
113
          for( int l : midLevels )
114
            if( l>maxLevel ) maxLevel = l;
115
        }
116 15d1f6ad Leszek Koltunski
117 7363eaa6 Leszek Koltunski
      createTablebase(maxLevel);
118 f079bf77 Leszek Koltunski
      mMidTables = numMidLevels >0 ? new PruningTable[numMidLevels ] : null;
119
      mHighTables= numHighLevels>0 ? new PruningTable[numHighLevels] : null;
120 15d1f6ad Leszek Koltunski
121 f079bf77 Leszek Koltunski
      for(int i=0; i<numMidLevels; i++)
122
        mMidTables[i] = new PruningTable(mTablebase,midLevels[i]);
123
      for(int i=0; i<numHighLevels; i++)
124
        mHighTables[i] = new PruningTable(mTablebase,highLevels[i]);
125 15d1f6ad Leszek Koltunski
126 bdcb662f Leszek Koltunski
      computeLowHigh();
127
128 7363eaa6 Leszek Koltunski
      mInitialized = true;
129 15d1f6ad Leszek Koltunski
      }
130
131 f079bf77 Leszek Koltunski
    int midNum  = mMidTables !=null ? mMidTables.length  : 0;
132
    int highNum = mHighTables!=null ? mHighTables.length : 0;
133
    byte[][] data = new byte[midNum+highNum][];
134
135
    for(int i=0; i<midNum ; i++) data[i       ] = mMidTables[i].getPacked();
136
    for(int i=0; i<highNum; i++) data[i+midNum] = mHighTables[i].getPacked();
137 7363eaa6 Leszek Koltunski
138 15d1f6ad Leszek Koltunski
    return data;
139
    }
140
141 00947987 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
142
143 bdcb662f Leszek Koltunski
  private void computeLowHigh()
144
    {
145
    mLowestHigh = mGodsNumber+1;
146
    int numHigh = mHighTables==null ? 0 : mHighTables.length;
147
148
    for( int i=0; i<numHigh; i++ )
149
      {
150
      int level = mHighTables[i].getLevel();
151
      if( i==0 || level<mLowestHigh ) mLowestHigh=level;
152
      }
153
154
    mLowestMid = 0;
155
    mHighestMid= 0;
156
    int numMid = mMidTables==null ? 0 : mMidTables.length;
157
158
    for( int i=0; i<numMid; i++ )
159
      {
160
      int level = mMidTables[i].getLevel();
161
      if( i==0 || level<mLowestMid  ) mLowestMid =level;
162
      if( i==0 || level>mHighestMid ) mHighestMid=level;
163
      }
164
    }
165
166
///////////////////////////////////////////////////////////////////////////////////////////////////
167
168
  public int traverseDownTable(int index, PruningTable[] tables, int tableIndex, int lastA, int lastR, int[] move, int[] tmpQuats)
169
    {
170
    int[] quats = getQuats(index);
171
    int numQuats = quats.length;
172
173
    move[0]=0;
174
    move[1]=0;
175
    move[2]=1;
176
    move[3]=1;
177
178
    for(int ax=0; ax<mNumAxis; ax++)
179
      for(int cubit=0; cubit<mNumCubits; cubit++)
180
        mRotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
181
182
    for(int s=0; s<mScalingFactor; s++)
183
      {
184
      int ax    = move[0];
185
      int layer = move[1];
186
      int quat  = move[3];
187
188
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
189
        {
190
        int bitLayer = (1<<layer);
191
        System.arraycopy(quats, 0, tmpQuats, 0, numQuats);
192
193
        for(int cubit=0; cubit<mNumCubits; cubit++)
194
          if( mRotRow[cubit][ax]==bitLayer )
195
            {
196
            int currQuat = tmpQuats[cubit];
197
            int newQuat = getMultQuat(quat,currQuat);
198
            tmpQuats[cubit] = newQuat;
199
            }
200
201
        int childIndex = getIndex(tmpQuats);
202
203
        if( tableIndex>0 )
204
          {
205
          if( tables[tableIndex-1].contains(childIndex) ) return childIndex;
206
          }
207
        else if( !tables[0].contains(childIndex) )
208
          {
209
          if( tables.length<=1 || !tables[1].contains(childIndex) ) return childIndex;
210
          }
211
        }
212
213
      getNextAxisLayerAngleQuat(move);
214
      }
215
216
    return -1;
217
    }
218
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220
221
  private int traversePruningBlock(int[][] moves, PruningTable[] tables, int tableIndex, int index, int lastA, int lastR)
222 00947987 Leszek Koltunski
    {
223 bdcb662f Leszek Koltunski
    int movesIndex = 1;
224 aa0757b6 Leszek Koltunski
    int[] move, tmpQuats = new int[mNumCubits];
225 bdcb662f Leszek Koltunski
226
    do
227
      {
228 aa0757b6 Leszek Koltunski
      move  = moves[movesIndex++];
229 bdcb662f Leszek Koltunski
      index = traverseDownTable(index,tables,tableIndex,lastA,lastR,move,tmpQuats);
230
      lastA = move[0];
231
      lastR = move[1];
232
      tableIndex--;
233
      }
234
    while( tableIndex>=0 );
235
236
    return index;
237
    }
238
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240
// ret: [0][] --> (new index,new depth,?,?) ; [1...N-1][] --> moves. (or null if index not in High)
241
242
  private int[][] traverseBlock(int index, PruningTable[] tables, int lastA, int lastR)
243
    {
244
    int num = (tables==null) ? 0 : tables.length;
245
    int tableIndex = -1;
246
247
    for( int i=0; i<num; i++ )
248
      if( tables[i].contains(index) )
249
        {
250
        tableIndex = i;
251
        break;
252
        }
253
254 aa0757b6 Leszek Koltunski
    if( tableIndex<0 )
255
      {
256
      android.util.Log.e("D", "block doesn't contain index "+index);
257
      return null;
258
      }
259 bdcb662f Leszek Koltunski
260
    int[][] ret = new int[tableIndex+2][4];
261
    ret[0][1] = tables[0].getLevel()-1;
262
    ret[0][0] = traversePruningBlock(ret,tables,tableIndex,index,lastA, lastR);
263
264
    return ret;
265
    }
266
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268
269
  private int midTablesContain(int index)
270
    {
271
    int num = mMidTables.length;
272
273
    for( int i=0; i<num; i++ )
274
      if( mMidTables[i].contains(index) )
275
        return i;
276
277
    return -1;
278
    }
279
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281
282
  private boolean jumpMidZeroRecursive(int index, int jump, int depth, int lastA, int lastR, int[][] solution)
283
    {
284 aa0757b6 Leszek Koltunski
    int[] quats   = getQuats(index);
285
    int numQuats  = quats.length;
286
    int[] move    = solution[depth];
287
    int[] tmp     = new int[mNumCubits];
288
    int[][] rotRow= new int[mNumCubits][mNumAxis];
289 bdcb662f Leszek Koltunski
290
    move[0]=0;
291
    move[1]=0;
292
    move[2]=1;
293
    move[3]=1;
294
295
    for(int ax=0; ax<mNumAxis; ax++)
296
      for(int cubit=0; cubit<mNumCubits; cubit++)
297 aa0757b6 Leszek Koltunski
        rotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
298 bdcb662f Leszek Koltunski
299
    for(int s=0; s<mScalingFactor; s++)
300
      {
301
      int ax    = move[0];
302
      int layer = move[1];
303
      int quat  = move[3];
304
305
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,ax,layer) )
306
        {
307
        int bitLayer = (1<<layer);
308
        System.arraycopy(quats, 0, tmp, 0, numQuats);
309
310
        for(int cubit=0; cubit<mNumCubits; cubit++)
311 aa0757b6 Leszek Koltunski
          if( rotRow[cubit][ax]==bitLayer )
312 bdcb662f Leszek Koltunski
            {
313
            int currQuat = tmp[cubit];
314
            int newQuat = getMultQuat(quat,currQuat);
315
            tmp[cubit] = newQuat;
316
            }
317
318
        int childIndex = getIndex(tmp);
319
320
        if( childIndex==0 )
321
          {
322 aa0757b6 Leszek Koltunski
          android.util.Log.e("D", "1 jumped to mid: index="+childIndex);
323
324 bdcb662f Leszek Koltunski
          solution[0][0] = childIndex;
325
          solution[0][1] = 0;
326
          return true;
327
          }
328
329
        int containingTable = midTablesContain(childIndex);
330
331
        if( containingTable>=0 )
332
          {
333 aa0757b6 Leszek Koltunski
          android.util.Log.e("D", "2 jumped to mid: index="+childIndex);
334
335 bdcb662f Leszek Koltunski
          solution[0][0] = childIndex;
336
          solution[0][1] = mMidTables[containingTable].getLevel();
337
          return true;
338
          }
339
340
        if( jump>1 && jumpMidZeroRecursive(childIndex, jump-1, depth+1, ax, layer, solution) )
341
          {
342
          return true;
343
          }
344
        }
345
346
      getNextAxisLayerAngleQuat(move);
347
      }
348
349
    return false;
350
    }
351
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353 aa0757b6 Leszek Koltunski
// ret: [0][] --> (num moves,old depth,?,?) ; [1...N-1][] --> moves.
354 bdcb662f Leszek Koltunski
355
  private int[][] jumpToMidOrZero(int index, int maxJump, int lastA, int lastR)
356
    {
357 aa0757b6 Leszek Koltunski
    if( midTablesContain(index)>=0 ) return null;
358
359 bdcb662f Leszek Koltunski
    int[][] solution = new int[maxJump+1][4];
360
361
    for(int i=1; i<maxJump; i++)
362
      if( jumpMidZeroRecursive(index,i,1,lastA,lastR,solution) )
363 aa0757b6 Leszek Koltunski
        {
364
        solution[0][0] = i;
365 bdcb662f Leszek Koltunski
        return solution;
366 aa0757b6 Leszek Koltunski
        }
367 bdcb662f Leszek Koltunski
368
    return null;
369
    }
370
371
///////////////////////////////////////////////////////////////////////////////////////////////////
372
373
  private boolean jumpZeroRecursive(int index, int jump, int depth, int lastA, int lastR, int[][] solution)
374
    {
375
    int[] quats  = getQuats(index);
376
    int numQuats = quats.length;
377
    int[] move   = solution[depth];
378
    int[] tmp    = new int[mNumCubits];
379 aa0757b6 Leszek Koltunski
    int[][]rotRow= new int[mNumCubits][mNumAxis];
380 bdcb662f Leszek Koltunski
381
    move[0]=0;
382
    move[1]=0;
383
    move[2]=1;
384
    move[3]=1;
385
386
    for(int ax=0; ax<mNumAxis; ax++)
387
      for(int cubit=0; cubit<mNumCubits; cubit++)
388 aa0757b6 Leszek Koltunski
        rotRow[cubit][ax] = computeRow(mPosition[cubit],quats[cubit],ax);
389
/*
390
if( index==1719861 )
391
  {
392
StringBuilder sb4 = new StringBuilder();
393
  for(int a=0; a<mNumAxis; a++)
394
    {
395
      for(int c=0; c<mNumCubits; c++)
396
        {
397
        sb4.append(mRotRow[c][a]);
398
        sb4.append(' ');
399
        }
400
    sb4.append("\n");
401 bdcb662f Leszek Koltunski
402 aa0757b6 Leszek Koltunski
    }
403
  android.util.Log.e("D", "1 rotRows= \n"+sb4 );
404
  }
405
*/
406 bdcb662f Leszek Koltunski
    for(int s=0; s<mScalingFactor; s++)
407
      {
408
      int ax    = move[0];
409
      int layer = move[1];
410
      int quat  = move[3];
411
412
      if( mRotatable[ax][layer] && moveCanProceed(lastA,lastR,move[0],move[1]) )
413
        {
414
        int bitLayer = (1<<layer);
415
        System.arraycopy(quats, 0, tmp, 0, numQuats);
416
417
        for(int cubit=0; cubit<mNumCubits; cubit++)
418 aa0757b6 Leszek Koltunski
          if( rotRow[cubit][ax]==bitLayer )
419 bdcb662f Leszek Koltunski
            {
420
            int currQuat = tmp[cubit];
421
            int newQuat = getMultQuat(quat,currQuat);
422
            tmp[cubit] = newQuat;
423
            }
424 aa0757b6 Leszek Koltunski
/*
425
if( depth==1 && ax==1 && layer==1 && move[2]==3 )
426
  {
427
  StringBuilder sb2 = new StringBuilder();
428
  for(int i=0; i<8; i++) { sb2.append(' '); sb2.append(quats[i]); }
429
  android.util.Log.e("D", " quats= "+sb2 );
430
431
  StringBuilder sb3 = new StringBuilder();
432
  for(int i=0; i<8; i++) { sb3.append(' '); sb3.append(tmp[i]); }
433
  android.util.Log.e("D", " tmpQuats= "+sb3 );
434
435
436
437
  android.util.Log.e("D", " index= "+index );
438
  }
439
440
441
if( index==1719861 )
442
  {
443
StringBuilder sb4 = new StringBuilder();
444
  for(int a=0; a<mNumAxis; a++)
445
    {
446
      for(int c=0; c<mNumCubits; c++)
447
        {
448
        sb4.append(mRotRow[c][a]);
449
        sb4.append(' ');
450
        }
451
    sb4.append("\n");
452
453
    }
454
  android.util.Log.e("D", "2 rotRows= \n"+sb4 );
455
  }
456
*/
457 bdcb662f Leszek Koltunski
458
        int childIndex = getIndex(tmp);
459 aa0757b6 Leszek Koltunski
        if( childIndex==0 ) return true;
460
        if( jump>1 && jumpZeroRecursive(childIndex, jump-1, depth+1, ax, layer, solution) ) return true;
461 bdcb662f Leszek Koltunski
        }
462
463
      getNextAxisLayerAngleQuat(move);
464
      }
465
466
    return false;
467
    }
468
469
///////////////////////////////////////////////////////////////////////////////////////////////////
470 aa0757b6 Leszek Koltunski
// ret: [0][] --> (numMoves,old depth,?,?) ; [1...N-1][] --> moves.
471 bdcb662f Leszek Koltunski
472
  private int[][] jumpToZero(int index, int maxJump, int lastA, int lastR)
473
    {
474
    int[][] solution = new int[maxJump+1][4];
475
476
    for(int i=1; i<maxJump; i++)
477
      if( jumpZeroRecursive(index,i,1,lastA,lastR,solution) )
478 aa0757b6 Leszek Koltunski
        {
479
        solution[0][0] = i;
480 bdcb662f Leszek Koltunski
        return solution;
481 aa0757b6 Leszek Koltunski
        }
482 bdcb662f Leszek Koltunski
483 00947987 Leszek Koltunski
    return null;
484
    }
485 bdcb662f Leszek Koltunski
486
///////////////////////////////////////////////////////////////////////////////////////////////////
487
488
  private int[][] concatenateMoves(int[][] high, int[][] jump1, int[][] mid, int[][] jump2)
489
    {
490
    int len1 = high ==null ? 0 : high.length-1;
491 aa0757b6 Leszek Koltunski
    int len2 = jump1==null ? 0 : jump1[0][0];
492 bdcb662f Leszek Koltunski
    int len3 = mid  ==null ? 0 : mid.length-1;
493 aa0757b6 Leszek Koltunski
    int len4 = jump2==null ? 0 : jump2[0][0];
494 bdcb662f Leszek Koltunski
495
    int[][] moves = new int[len1+len2+len3+len4][];
496
    int index = 0;
497
498
    for(int i=0; i<len1; i++) moves[index++] =  high[i+1];
499
    for(int i=0; i<len2; i++) moves[index++] = jump1[i+1];
500
    for(int i=0; i<len3; i++) moves[index++] =   mid[i+1];
501
    for(int i=0; i<len4; i++) moves[index++] = jump2[i+1];
502
503
    convertMoves(moves);
504
505
    return moves;
506
    }
507
508
///////////////////////////////////////////////////////////////////////////////////////////////////
509
510
  public int[][] solution(int index, int[] extra)
511
    {
512
    if( index==0 ) return null;
513
    int lastA=-1, lastR=0;
514
    int[][] highMoves = traverseBlock(index,mHighTables,lastA,lastR);
515
516
    if( highMoves!=null )
517
      {
518
      index = highMoves[0][0];
519
      int len = highMoves.length;
520
      lastA = highMoves[len-1][0];
521
      lastR = highMoves[len-1][1];
522
      }
523
524
    int maxJump = Math.max(mLowestHigh-1-mHighestMid,mLowestMid/2);
525
    int[][] jump1Moves = jumpToMidOrZero(index,maxJump,lastA,lastR);
526
527
    if( jump1Moves!=null )
528
      {
529
      if( jump1Moves[0][0]==0 )
530
        {
531
        return concatenateMoves(null,null,null,jump1Moves);
532
        }
533
      if( jump1Moves[0][1]==mLowestMid )
534
        {
535
        int[][] jump2Moves = jumpToZero(index,mLowestMid-1,-1,0);
536
        if( jump2Moves==null ) throw new RuntimeException("1 error jumping to 0");
537
        return concatenateMoves(null,null,null,jump2Moves);
538
        }
539
540
      index = jump1Moves[0][0];
541
      int len = jump1Moves.length;
542
      lastA = jump1Moves[len-1][0];
543
      lastR = jump1Moves[len-1][1];
544
      }
545
546
    int[][] midMoves = traverseBlock(index,mMidTables,lastA,lastR);
547
    if( midMoves!=null )
548
      {
549
      index = midMoves[0][0];
550
      int len = midMoves.length;
551
      lastA = midMoves[len-1][0];
552
      lastR = midMoves[len-1][1];
553
      }
554
    else throw new RuntimeException("error traversing mid Tables");
555
556 aa0757b6 Leszek Koltunski
android.util.Log.e("D", "");
557
558 bdcb662f Leszek Koltunski
    int[][] jump2Moves = jumpToZero(index,mLowestMid-1,lastA,lastR);
559
    if( jump2Moves==null ) throw new RuntimeException("2 error jumping to 0");
560
    return concatenateMoves(highMoves,jump1Moves,midMoves,jump2Moves);
561
    }
562 00947987 Leszek Koltunski
}