Project

General

Profile

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

magiccube / src / main / java / org / distorted / effects / scramble / ScrambleEffect.java @ 36b9ee93

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.effects.scramble;
21

    
22
import org.distorted.effects.BaseEffect;
23
import org.distorted.library.effect.Effect;
24
import org.distorted.library.main.DistortedEffects;
25
import org.distorted.library.main.DistortedScreen;
26
import org.distorted.library.message.EffectListener;
27
import org.distorted.main.RubikPreRender;
28
import org.distorted.effects.EffectController;
29
import org.distorted.objects.TwistyObject;
30

    
31
import java.lang.reflect.Method;
32
import java.util.Random;
33

    
34
///////////////////////////////////////////////////////////////////////////////////////////////////
35

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

    
44
    final Class<? extends ScrambleEffect> effect;
45

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

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

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

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

    
67
  public static final int START_AXIS = -2;
68
  public static final int STOP_AXIS  = -1;
69

    
70
  private EffectController mController;
71
  private int mEffectReturned;
72
  private int mNumDoubleScramblesLeft, mNumScramblesLeft;
73
  private int mLastRotAxis, mLastRow;
74
  private long mDurationSingleTurn;
75
  private final Random mRnd;
76
  private int mBasicAngle;
77

    
78
  TwistyObject mObject;
79
  Effect[] mNodeEffects;
80
  int[] mNodeEffectPosition;
81
  Effect[] mCubeEffects;
82
  int[] mCubeEffectPosition;
83
  int mCubeEffectNumber, mNodeEffectNumber;
84

    
85
///////////////////////////////////////////////////////////////////////////////////////////////////
86

    
87
  ScrambleEffect()
88
    {
89
    mRnd = new Random( System.currentTimeMillis() );
90
    mLastRotAxis = START_AXIS;
91
    }
92

    
93
///////////////////////////////////////////////////////////////////////////////////////////////////
94

    
95
  abstract void createEffects(int duration, int numScrambles);
96
  abstract void effectFinishedPlugin(final long effectID);
97

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

    
108
  private void createBaseEffects(int duration, int numScrambles)
109
    {
110
    mNumScramblesLeft = numScrambles;
111

    
112
    mNumDoubleScramblesLeft=0;
113

    
114
    if( mBasicAngle>=4 )
115
      {
116
      int chance = mBasicAngle==4 ? 3:2;
117

    
118
      for(int i=0; i<numScrambles; i++)
119
        {
120
        if( (mRnd.nextInt() % chance) == 0 )
121
          {
122
          mNumDoubleScramblesLeft++;
123
          }
124
        }
125
      }
126

    
127
    mDurationSingleTurn = duration/(mNumScramblesLeft+mNumDoubleScramblesLeft);
128

    
129
    addNewScramble();
130
    }
131

    
132
///////////////////////////////////////////////////////////////////////////////////////////////////
133
// only works if basicAngle<=5
134

    
135
  private void addNewScramble()
136
    {
137
    if( mNumScramblesLeft>0 )
138
      {
139
      int lastRotAxis = mLastRotAxis;
140
      mLastRotAxis = mObject.randomizeNewRotAxis(mRnd,mLastRotAxis);
141
      mLastRow = mObject.randomizeNewRow(mRnd,lastRotAxis,mLastRow,mLastRotAxis);
142

    
143
      int rowBitmap  = (1<<mLastRow);
144
      int angle= randomizeAngle();
145
      int absAngle = (angle<0 ? -angle : angle);
146
      long durationMillis = absAngle*mDurationSingleTurn;
147

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

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

    
156
      mController.addRotation(this, mLastRotAxis, rowBitmap, angle*(360/mBasicAngle), durationMillis);
157
      }
158
    else
159
      {
160
      mLastRotAxis = STOP_AXIS;
161

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

    
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170
// only works for basicAngle<=5.
171

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

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

    
181
///////////////////////////////////////////////////////////////////////////////////////////////////
182

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

    
191
    DistortedEffects nodeEffects = mObject.getEffects();
192

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

    
200
///////////////////////////////////////////////////////////////////////////////////////////////////
201

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

    
209
    DistortedEffects nodeEffects = mObject.getEffects();
210

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

    
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218
// PUBLIC API
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220

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

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

    
231
    return names;
232
    }
233

    
234
///////////////////////////////////////////////////////////////////////////////////////////////////
235

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

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

    
244
  public void onActionFinished(final long effectID)
245
    {
246
    addNewScramble();
247
    }
248

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

    
251
  public void effectFinished(final long effectID)
252
    {
253
    for(int i=0; i<mCubeEffectNumber; i++)
254
      {
255
      long id = mCubeEffects[i].getID();
256

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

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

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

    
272
        return;
273
        }
274
      }
275

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

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

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

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

    
295
        return;
296
        }
297
      }
298
    }
299

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301

    
302
  @SuppressWarnings("unused")
303
  public long start(int duration, DistortedScreen screen, EffectController cont)
304
    {
305
    mObject    = cont.getObject();
306
    mController= cont;
307

    
308
    mObject.solve();
309

    
310
    mBasicAngle = mObject.getBasicAngle();
311

    
312
    int numScrambles = cont.getNumScrambles();
313
    int dura = (int)(duration*Math.pow(numScrambles,0.6f));
314
    createBaseEffects(dura,numScrambles);
315
    createEffects    (dura,numScrambles);
316

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

    
322
    assignEffects();
323

    
324
    return FAKE_EFFECT_ID;
325
    }
326

    
327
///////////////////////////////////////////////////////////////////////////////////////////////////
328

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

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

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