Project

General

Profile

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

magiccube / src / main / java / org / distorted / effect / scramble / ScrambleEffect.java @ 8becce57

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.main.DistortedScreen;
26
import org.distorted.library.message.EffectListener;
27
import org.distorted.magic.RubikPostRender;
28
import org.distorted.object.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

    
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
      mPostRender.addRotation(this, mLastVector, rowBitmap, angle*(360/mBasicAngle), durationMillis);
158
      }
159
    else
160
      {
161
      mLastVector = -1;
162

    
163
      if( mEffectReturned == mCubeEffectNumber+mNodeEffectNumber )
164
        {
165
        mPostRender.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 onActionFinished(final long effectID)
246
    {
247
    mObject.removeRotationNow();
248
    addNewScramble();
249
    }
250

    
251
///////////////////////////////////////////////////////////////////////////////////////////////////
252

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

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

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

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

    
274
        return;
275
        }
276
      }
277

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

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

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

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

    
297
        return;
298
        }
299
      }
300
    }
301

    
302
///////////////////////////////////////////////////////////////////////////////////////////////////
303

    
304
  @SuppressWarnings("unused")
305
  public long start(int duration, DistortedScreen screen, RubikPostRender post)
306
    {
307
    mObject     = post.getObject();
308
    mPostRender = post;
309

    
310
    mObject.solve();
311

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

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

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

    
325
    assignEffects();
326

    
327
    return FAKE_EFFECT_ID;
328
    }
329

    
330
///////////////////////////////////////////////////////////////////////////////////////////////////
331

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

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

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