Project

General

Profile

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

magiccube / src / main / java / org / distorted / effects / scramble / ScrambleEffect.java @ 5cf34c5f

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.RubikPostRender;
28
import org.distorted.objects.RubikObject;
29

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

    
33
///////////////////////////////////////////////////////////////////////////////////////////////////
34

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

    
43
    final Class<? extends ScrambleEffect> effect;
44

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

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

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

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

    
66
  private RubikPostRender mPostRender;
67
  private int mEffectReturned;
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
  private float[] mRowChances;
76

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

    
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84

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

    
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92

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

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

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

    
109
    mNumDoubleScramblesLeft=0;
110

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

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

    
124
    addNewScramble();
125
    }
126

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

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

    
145
      int row=0;
146
      float rowFloat = mRnd.nextFloat();
147

    
148
      for(int i=0; i<mRowChances.length; i++)
149
        {
150
        if( rowFloat<=mRowChances[i] )
151
          {
152
          row=i;
153
          break;
154
          }
155
        }
156

    
157
      int rowBitmap  = (1<<row);
158
      int angle= randomizeAngle();
159
      int absAngle = (angle<0 ? -angle : angle);
160
      long durationMillis = absAngle*mDurationSingleTurn;
161

    
162
      mNumScramblesLeft--;
163
      if( absAngle==2 ) mNumDoubleScramblesLeft--;
164

    
165
      if( mNumScramblesLeft==0 && mNumDoubleScramblesLeft!=0 )
166
        {
167
        android.util.Log.e("effect", "ERROR: "+mNumDoubleScramblesLeft);
168
        }
169

    
170
      mPostRender.addRotation(this, mLastVector, rowBitmap, angle*(360/mBasicAngle), durationMillis);
171
      }
172
    else
173
      {
174
      mLastVector = -1;
175

    
176
      if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
177
        {
178
        mPostRender.effectFinished(FAKE_EFFECT_ID);
179
        }
180
      }
181
    }
182

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184
// only works for basicAngle<=4.
185

    
186
  private int randomizeAngle()
187
    {
188
    int random = mRnd.nextInt(mNumScramblesLeft);
189
    int result = random<mNumDoubleScramblesLeft ? 2:1;
190
    int sign   = mRnd.nextInt(2);
191

    
192
    return sign==0 ? result : -result;
193
    }
194

    
195
///////////////////////////////////////////////////////////////////////////////////////////////////
196

    
197
  private void assignEffects()
198
    {
199
    for(int i=0; i<mCubeEffectNumber; i++)
200
      {
201
      mObject.apply(mCubeEffects[i],mCubeEffectPosition[i]);
202
      mCubeEffects[i].notifyWhenFinished(this);
203
      }
204

    
205
    DistortedEffects nodeEffects = mObject.getEffects();
206

    
207
    for(int i=0; i<mNodeEffectNumber; i++)
208
      {
209
      nodeEffects.apply(mNodeEffects[i],mNodeEffectPosition[i]);
210
      mNodeEffects[i].notifyWhenFinished(this);
211
      }
212
    }
213

    
214
///////////////////////////////////////////////////////////////////////////////////////////////////
215

    
216
  private void disassignEffects()
217
    {
218
    for(int i=0; i<mCubeEffectNumber; i++)
219
      {
220
      mObject.remove(mCubeEffects[i].getID());
221
      }
222

    
223
    DistortedEffects nodeEffects = mObject.getEffects();
224

    
225
    for(int i=0; i<mNodeEffectNumber; i++)
226
      {
227
      nodeEffects.abortById(mNodeEffects[i].getID());
228
      }
229
    }
230

    
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232
// PUBLIC API
233
///////////////////////////////////////////////////////////////////////////////////////////////////
234

    
235
  @SuppressWarnings("unused")
236
  public static String[] getNames()
237
    {
238
    String[] names = new String[NUM_EFFECTS];
239

    
240
    for( int i=0; i<NUM_EFFECTS; i++)
241
      {
242
      names[i] = types[i].name();
243
      }
244

    
245
    return names;
246
    }
247

    
248
///////////////////////////////////////////////////////////////////////////////////////////////////
249

    
250
  @SuppressWarnings("unused")
251
  public static ScrambleEffect create(int ordinal) throws InstantiationException, IllegalAccessException
252
    {
253
    return types[ordinal].effect.newInstance();
254
    }
255

    
256
///////////////////////////////////////////////////////////////////////////////////////////////////
257

    
258
  public void onActionFinished(final long effectID)
259
    {
260
    addNewScramble();
261
    }
262

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264

    
265
  public void effectFinished(final long effectID)
266
    {
267
    for(int i=0; i<mCubeEffectNumber; i++)
268
      {
269
      long id = mCubeEffects[i].getID();
270

    
271
      if( effectID == id )
272
        {
273
        mEffectReturned++;
274
        effectFinishedPlugin(effectID);
275

    
276
        if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
277
          {
278
          disassignEffects();
279

    
280
          if( mNumScramblesLeft==0 )
281
            {
282
            mPostRender.effectFinished(FAKE_EFFECT_ID);
283
            }
284
          }
285

    
286
        return;
287
        }
288
      }
289

    
290
    for(int i=0; i<mNodeEffectNumber; i++)
291
      {
292
      long id = mNodeEffects[i].getID();
293

    
294
      if( effectID == id )
295
        {
296
        mEffectReturned++;
297
        effectFinishedPlugin(effectID);
298

    
299
        if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
300
          {
301
          disassignEffects();
302

    
303
          if( mNumScramblesLeft==0 )
304
            {
305
            mPostRender.effectFinished(FAKE_EFFECT_ID);
306
            }
307
          }
308

    
309
        return;
310
        }
311
      }
312
    }
313

    
314
///////////////////////////////////////////////////////////////////////////////////////////////////
315

    
316
  @SuppressWarnings("unused")
317
  public long start(int duration, DistortedScreen screen, RubikPostRender post)
318
    {
319
    mObject     = post.getObject();
320
    mPostRender = post;
321
    mRowChances = mObject.getRowChances();
322

    
323
    mObject.solve();
324

    
325
    mNumAxis    = mObject.getRotationAxis().length;
326
    mBasicAngle = mObject.getBasicAngle();
327

    
328
    int numScrambles = post.getNumScrambles();
329
    int dura = (int)(duration*Math.pow(numScrambles,0.6f));
330
    createBaseEffects(dura,numScrambles);
331
    createEffects    (dura,numScrambles);
332

    
333
    if( mCubeEffectNumber==0 && mNodeEffectNumber==0 )
334
      {
335
      throw new RuntimeException("Cube and Node Plugin Effects not created!");
336
      }
337

    
338
    assignEffects();
339

    
340
    return FAKE_EFFECT_ID;
341
    }
342

    
343
///////////////////////////////////////////////////////////////////////////////////////////////////
344

    
345
  @SuppressWarnings("unused")
346
  public static void enableEffects()
347
    {
348
    Method method;
349

    
350
    for(Type type: Type.values())
351
      {
352
      try
353
        {
354
        method = type.effect.getDeclaredMethod("enable"); // enable not public, thus getDeclaredMethod
355
        }
356
      catch(NoSuchMethodException ex)
357
        {
358
        android.util.Log.e("ScrambleEffect", type.effect.getSimpleName()+": exception getting method: "+ex.getMessage());
359
        method = null;
360
        }
361

    
362
      try
363
        {
364
        if( method!=null ) method.invoke(null);
365
        }
366
      catch(Exception ex)
367
        {
368
        android.util.Log.e("ScrambleEffect", type.effect.getSimpleName()+": exception invoking method: "+ex.getMessage());
369
        }
370
      }
371
    }
372
}
(1-1/3)