Project

General

Profile

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

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

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.objects.TwistyObject;
29

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

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

    
35
public abstract class ScrambleEffect extends BaseEffect implements EffectListener, RubikPreRender.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
  public static final int START_AXIS = -2;
67
  public static final int STOP_AXIS  = -1;
68

    
69
  private RubikPreRender mPreRender;
70
  private int mEffectReturned;
71
  private int mNumDoubleScramblesLeft, mNumScramblesLeft;
72
  private int mLastRotAxis, mLastRow;
73
  private long mDurationSingleTurn;
74
  private Random mRnd;
75
  private int mBasicAngle;
76

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

    
84
///////////////////////////////////////////////////////////////////////////////////////////////////
85

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

    
92
///////////////////////////////////////////////////////////////////////////////////////////////////
93

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

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

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

    
111
    mNumDoubleScramblesLeft=0;
112

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

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

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

    
128
    addNewScramble();
129
    }
130

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

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

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

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

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

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

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

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

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

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

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

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

    
190
    DistortedEffects nodeEffects = mObject.getEffects();
191

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

    
199
///////////////////////////////////////////////////////////////////////////////////////////////////
200

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

    
208
    DistortedEffects nodeEffects = mObject.getEffects();
209

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

    
216
///////////////////////////////////////////////////////////////////////////////////////////////////
217
// PUBLIC API
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219

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

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

    
230
    return names;
231
    }
232

    
233
///////////////////////////////////////////////////////////////////////////////////////////////////
234

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

    
241
///////////////////////////////////////////////////////////////////////////////////////////////////
242

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

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

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

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

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

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

    
271
        return;
272
        }
273
      }
274

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

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

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

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

    
294
        return;
295
        }
296
      }
297
    }
298

    
299
///////////////////////////////////////////////////////////////////////////////////////////////////
300

    
301
  @SuppressWarnings("unused")
302
  public long start(int duration, DistortedScreen screen, RubikPreRender pre)
303
    {
304
    mObject     = pre.getObject();
305
    mPreRender  = pre;
306

    
307
    mObject.solve();
308

    
309
    mBasicAngle = mObject.getBasicAngle();
310

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

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

    
321
    assignEffects();
322

    
323
    return FAKE_EFFECT_ID;
324
    }
325

    
326
///////////////////////////////////////////////////////////////////////////////////////////////////
327

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

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

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