Project

General

Profile

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

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

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
  private int mSize;
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 rowBitmap  = (1<<mRnd.nextInt(mSize));
146
      int angle= randomizeAngle();
147
      int absAngle = (angle<0 ? -angle : angle);
148
      long durationMillis =  absAngle*mDurationSingleTurn;
149

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

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

    
158
      mPostRender.addRotation(this, mLastVector, rowBitmap, angle*(360/mBasicAngle), durationMillis);
159
      }
160
    else
161
      {
162
      mLastVector = -1;
163

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

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

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

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

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184

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

    
193
    DistortedEffects nodeEffects = mObject.getEffects();
194

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

    
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203

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

    
211
    DistortedEffects nodeEffects = mObject.getEffects();
212

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

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220
// PUBLIC API
221
///////////////////////////////////////////////////////////////////////////////////////////////////
222

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

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

    
233
    return names;
234
    }
235

    
236
///////////////////////////////////////////////////////////////////////////////////////////////////
237

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

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245

    
246
  public void onActionFinished(final long effectID)
247
    {
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
    mSize       = mObject.getSize();
315

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

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

    
326
    assignEffects();
327

    
328
    return FAKE_EFFECT_ID;
329
    }
330

    
331
///////////////////////////////////////////////////////////////////////////////////////////////////
332

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

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

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