Project

General

Profile

Download (10.8 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / effect / scramble / ScrambleEffect.java @ 9224ffd2

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 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.effect.scramble;
21

    
22
import org.distorted.effect.BaseEffect;
23
import org.distorted.library.effect.Effect;
24
import org.distorted.library.main.DistortedEffects;
25
import org.distorted.library.message.EffectListener;
26
import org.distorted.magic.RubikRenderer;
27
import org.distorted.object.RubikObject;
28

    
29
import java.lang.reflect.Method;
30
import java.util.Random;
31

    
32
///////////////////////////////////////////////////////////////////////////////////////////////////
33

    
34
public abstract class ScrambleEffect extends BaseEffect implements EffectListener
35
{
36
  public enum Type
37
    {
38
    NONE         (ScrambleEffectNone.class        ),
39
    ROTATIONS    (ScrambleEffectRotations.class   ),
40
    ;
41

    
42
    final Class<? extends ScrambleEffect> effect;
43

    
44
    Type(Class<? extends ScrambleEffect> effect)
45
      {
46
      this.effect= effect;
47
      }
48
    }
49

    
50
  private static int NUM_EFFECTS = Type.values().length;
51
  private static final int FAKE_EFFECT_ID  = -3;
52
  private static final Type[] types;
53

    
54
  static
55
    {
56
    int i=0;
57
    types = new Type[NUM_EFFECTS];
58

    
59
    for(Type type: Type.values())
60
      {
61
      types[i++] = type;
62
      }
63
    }
64

    
65
  private EffectListener mListener;
66
  private int mEffectReturned;
67
  private long mCurrentBaseEffectID;
68
  private int mNumDoubleScramblesLeft, mNumScramblesLeft;
69
  private int mLastVector;
70
  private long mDurationSingleTurn;
71
  private Random mRnd;
72
  private RubikObject mObject;
73
  private int mNumAxis;
74
  private int mBasicAngle;
75

    
76
  Effect[] mNodeEffects;
77
  int[] mNodeEffectPosition;
78
  Effect[] mCubeEffects;
79
  int[] mCubeEffectPosition;
80
  int mCubeEffectNumber, mNodeEffectNumber;
81

    
82
///////////////////////////////////////////////////////////////////////////////////////////////////
83

    
84
  ScrambleEffect()
85
    {
86
    mRnd = new Random( System.currentTimeMillis() );
87
    mLastVector = -2;
88
    }
89

    
90
///////////////////////////////////////////////////////////////////////////////////////////////////
91

    
92
  abstract void createEffects(int duration, int numScrambles);
93
  abstract void effectFinishedPlugin(final long effectID);
94

    
95
///////////////////////////////////////////////////////////////////////////////////////////////////
96
// first compute how many out of 'numScrambles' are double turns (this will matter when we compute
97
// the time a single quarter-turn takes!
98
//
99
// Only works for
100
// basicAngle==4, i.e. something whose rotations are by  90 degrees (RubikCube) or
101
// basicAngle==3, i.e. something whose rotations are by 120 degrees (a Pyramix) or
102
// basicAngle==2, i.e. something whose rotations are by 180 degrees (e.g. a 3x2x1 'Bunny')
103

    
104
  private void createBaseEffects(int duration, int numScrambles)
105
    {
106
    mNumScramblesLeft = numScrambles;
107

    
108
    mNumDoubleScramblesLeft=0;
109

    
110
    if( mBasicAngle>=4 )
111
      {
112
      for(int i=0; i<numScrambles; i++)
113
        {
114
        if( (mRnd.nextInt() % 3) == 0 )
115
          {
116
          mNumDoubleScramblesLeft++;
117
          }
118
        }
119
      }
120

    
121
    mDurationSingleTurn = duration/(mNumScramblesLeft+mNumDoubleScramblesLeft);
122

    
123
    addNewScramble();
124
    }
125

    
126
///////////////////////////////////////////////////////////////////////////////////////////////////
127
// only works if basicAngle<=4, i.e. won't work for something whose basic rotations are by less
128
// than 90 degrees.
129

    
130
  private void addNewScramble()
131
    {
132
    if( mNumScramblesLeft>0 )
133
      {
134
      if( mLastVector == -2 )
135
        {
136
        mLastVector = mRnd.nextInt(mNumAxis);
137
        }
138
      else
139
        {
140
        int newVector = mRnd.nextInt(mNumAxis-1);
141
        mLastVector = (newVector>=mLastVector ? newVector+1 : newVector);
142
        }
143

    
144
      int rowBitmap  = (1<<mRnd.nextInt(mObject.getSize()));
145
      int angle= randomizeAngle();
146
      int absAngle = (angle<0 ? -angle : angle);
147
      long durationMillis =  absAngle*mDurationSingleTurn;
148

    
149
      mNumScramblesLeft--;
150
      if( absAngle==2 ) mNumDoubleScramblesLeft--;
151

    
152
      if( mNumScramblesLeft==0 && mNumDoubleScramblesLeft!=0 )
153
        {
154
        android.util.Log.e("effect", "ERROR: "+mNumDoubleScramblesLeft);
155
        }
156

    
157
      mCurrentBaseEffectID = mObject.addNewRotation(mLastVector, rowBitmap, angle*(360/mBasicAngle), durationMillis, this );
158
      }
159
    else
160
      {
161
      mLastVector = -1;
162

    
163
      if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
164
        {
165
        mListener.effectFinished(FAKE_EFFECT_ID);
166
        }
167
      }
168
    }
169

    
170
///////////////////////////////////////////////////////////////////////////////////////////////////
171
// only works for basicAngle<=4.
172

    
173
  private int randomizeAngle()
174
    {
175
    int random = mRnd.nextInt(mNumScramblesLeft);
176
    int result = random<mNumDoubleScramblesLeft ? 2:1;
177
    int sign   = mRnd.nextInt(2);
178

    
179
    return sign==0 ? result : -result;
180
    }
181

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
  private void assignEffects()
185
    {
186
    for(int i=0; i<mCubeEffectNumber; i++)
187
      {
188
      mObject.apply(mCubeEffects[i],mCubeEffectPosition[i]);
189
      mCubeEffects[i].notifyWhenFinished(this);
190
      }
191

    
192
    DistortedEffects nodeEffects = mObject.getEffects();
193

    
194
    for(int i=0; i<mNodeEffectNumber; i++)
195
      {
196
      nodeEffects.apply(mNodeEffects[i],mNodeEffectPosition[i]);
197
      mNodeEffects[i].notifyWhenFinished(this);
198
      }
199
    }
200

    
201
///////////////////////////////////////////////////////////////////////////////////////////////////
202

    
203
  private void disassignEffects()
204
    {
205
    for(int i=0; i<mCubeEffectNumber; i++)
206
      {
207
      mObject.remove(mCubeEffects[i].getID());
208
      }
209

    
210
    DistortedEffects nodeEffects = mObject.getEffects();
211

    
212
    for(int i=0; i<mNodeEffectNumber; i++)
213
      {
214
      nodeEffects.abortById(mNodeEffects[i].getID());
215
      }
216
    }
217

    
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219
// PUBLIC API
220
///////////////////////////////////////////////////////////////////////////////////////////////////
221

    
222
  @SuppressWarnings("unused")
223
  public static String[] getNames()
224
    {
225
    String[] names = new String[NUM_EFFECTS];
226

    
227
    for( int i=0; i<NUM_EFFECTS; i++)
228
      {
229
      names[i] = types[i].name();
230
      }
231

    
232
    return names;
233
    }
234

    
235
///////////////////////////////////////////////////////////////////////////////////////////////////
236

    
237
  @SuppressWarnings("unused")
238
  public static ScrambleEffect create(int ordinal) throws InstantiationException, IllegalAccessException
239
    {
240
    return types[ordinal].effect.newInstance();
241
    }
242

    
243
///////////////////////////////////////////////////////////////////////////////////////////////////
244

    
245
  public void effectFinished(final long effectID)
246
    {
247
    if( effectID == mCurrentBaseEffectID )
248
      {
249
      mObject.removeRotationNow();
250
      addNewScramble();
251
      return;
252
      }
253

    
254
    for(int i=0; i<mCubeEffectNumber; i++)
255
      {
256
      long id = mCubeEffects[i].getID();
257

    
258
      if( effectID == id )
259
        {
260
        mEffectReturned++;
261
        effectFinishedPlugin(effectID);
262

    
263
        if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
264
          {
265
          disassignEffects();
266

    
267
          if( mNumScramblesLeft==0 )
268
            {
269
            mListener.effectFinished(FAKE_EFFECT_ID);
270
            }
271
          }
272

    
273
        return;
274
        }
275
      }
276

    
277
    for(int i=0; i<mNodeEffectNumber; i++)
278
      {
279
      long id = mNodeEffects[i].getID();
280

    
281
      if( effectID == id )
282
        {
283
        mEffectReturned++;
284
        effectFinishedPlugin(effectID);
285

    
286
        if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
287
          {
288
          disassignEffects();
289

    
290
          if( mNumScramblesLeft==0 )
291
            {
292
            mListener.effectFinished(FAKE_EFFECT_ID);
293
            }
294
          }
295

    
296
        return;
297
        }
298
      }
299
    }
300

    
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302

    
303
  @SuppressWarnings("unused")
304
  public long start(int duration, RubikRenderer renderer)
305
    {
306
    mObject   = renderer.getObject();
307
    mListener = renderer;
308

    
309
    mObject.solve();
310

    
311
    mNumAxis    = mObject.getRotationAxis().length;
312
    mBasicAngle = mObject.getBasicAngle();
313

    
314
    int numScrambles = renderer.getNumScrambles();
315
    int dura = (int)(duration*Math.pow(numScrambles,0.6f));
316
    createBaseEffects(dura,numScrambles);
317
    createEffects    (dura,numScrambles);
318

    
319
    if( mCubeEffectNumber==0 && mNodeEffectNumber==0 )
320
      {
321
      throw new RuntimeException("Cube and Node Plugin Effects not created!");
322
      }
323

    
324
    assignEffects();
325

    
326
    return FAKE_EFFECT_ID;
327
    }
328

    
329
///////////////////////////////////////////////////////////////////////////////////////////////////
330

    
331
  @SuppressWarnings("unused")
332
  public static void enableEffects()
333
    {
334
    Method method;
335

    
336
    for(Type type: Type.values())
337
      {
338
      try
339
        {
340
        method = type.effect.getDeclaredMethod("enable"); // enable not public, thus getDeclaredMethod
341
        }
342
      catch(NoSuchMethodException ex)
343
        {
344
        android.util.Log.e("ScrambleEffect", type.effect.getSimpleName()+": exception getting method: "+ex.getMessage());
345
        method = null;
346
        }
347

    
348
      try
349
        {
350
        if( method!=null ) method.invoke(null);
351
        }
352
      catch(Exception ex)
353
        {
354
        android.util.Log.e("ScrambleEffect", type.effect.getSimpleName()+": exception invoking method: "+ex.getMessage());
355
        }
356
      }
357
    }
358
}
(1-1/3)