Back to project page candy-drop.
The source code is released under:
Copyright (c) 2014, Gregory Martin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: ...
If you think the Android project candy-drop listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.gregfmartin.facetapper.utils; //w w w . ja v a 2s .co m import com.gregfmartin.facetapper.entities.Pulse; import java.util.LinkedList; /** * A helper utility that will perform necessary calculations for certain parts in the game. * * @author Gregory Martin */ public final class MathEngine { static final private MathEngine instance = new MathEngine(); /** * The total number of levels in the game. */ static final public int MAX_LEVELS = 100; /** * The default number of continues the player starts off with */ static final public short BASE_PLAYER_CONTINUES = 3; /** * The current level the player is on. The default value for this is zero even though the player * will never be on level "zero" as it will be incremented before it is ever used. */ private byte mCurrentLevel; /** * The total number of candies to drop in this level. This value will change depending upon several * factors including, but not limited to, the difficulty. */ private short mTotalCandiesToDrop; /** * The number of candies that were dropped in the previous level. */ private short mPreviousLevelCandyTotal; /** * The total number of candies the player is required to tap on in order to complete the level. This * value is automatically generated as well. */ private short mPlayerTapThreshold; /** * The current number of candies the player has tapped on. */ private short mCurrentTappedCandies; /** * The player's current score. */ private int mScore; /** * The number of times a player can continue if they lose the round. */ private byte mContinues; /** * The Pulse that helps determine how to drop Candies */ private Pulse mCandyDropPulse; /** * A list of difficulties that will be used per level. These are all populated before they're ever used. */ private LinkedList<Float> mCanonicalDifficultyHistory; private MathEngine() { populateDifficultyScalars(); mScore = 0; mContinues = BASE_PLAYER_CONTINUES; } static final public MathEngine getInstance() { return instance; } public byte getLevel() { return mCurrentLevel; } public short getTotalCandies() { return mTotalCandiesToDrop; } public short getPlayerTapThreshold() { return mPlayerTapThreshold; } public short getNumTappedCandies() { return mCurrentTappedCandies; } public int getScore() { return mScore; } public byte getContinues() { return mContinues; } public void addToScore(int appendScore) { mScore += appendScore; } public void addToTotalTapped(int appendTap) { mCurrentTappedCandies += appendTap; } public void minusLevel() { mCurrentLevel--; } /** * This method will populate all of the possible difficulty scalars that will be used per level. */ private void populateDifficultyScalars() { mCanonicalDifficultyHistory = new LinkedList<>(); /* * ln(0) = Infinity/Undefined and ln(1) = 1. These are accounted for in the method that * actually calculates the scalar. */ for(int cur = 0; cur < MAX_LEVELS; cur++) { mCanonicalDifficultyHistory.add(generateLevelDifficultyScalar(cur)); } } public void update() { // Update the Pulses mCandyDropPulse.update(); } /** * Generates the difficulty scalar for the level that the player is currently on. The difficulty is * determined by a function of the natural logarithm (loge) against the current level along with some * tweaking to ensure sane values are returned. For example, ln(0) is Infinity or Undefined so we can't * use it and ln(1) = 1 so using that in calculations would result in a plateau effect. The only problem * with using a logarithmic curve for the difficulty is that the plateau effect arises again very quickly * in later levels as the differences in yield are of a magnitude of 0.001 or less in some cases. This needs * to be tested throughout the system to see if the reflected changes are usable or not. * * @param level The current level the player is on * @return A float representing the current degree of difficulty that will be applied to all entity-based * calculations in the system. The zero-index is accounted for automatically. */ private float generateLevelDifficultyScalar(int level) { return (float)Math.log(Math.pow(((level + 1) * 2), 2)); } /** * Calculate the total number of candies that will be distributed for this level. The total amount will * use the current difficulty as base factor so that the amount will scale logarithmically, giving the impression * that more candies will be assigned as the levels increase. If the player is on a level past the first one, * the previous total will also be used as a base factor in the calculation to help mitigate the plateau effect * on the logarithmic curve. * * @return The total number of candies that will be distributed on the current level */ private short calculateTotalCandies() { // Check first to see if we have a total value from a previous level if(0 == mPreviousLevelCandyTotal) { // Generate a fresh batch return (short)Math.floor(getDifficultyForLevel(mCurrentLevel)); } else { return (short)(Math.floor(getDifficultyForLevel(mCurrentLevel) + mPreviousLevelCandyTotal)); } } /** * @param level The current level the player is on * @return The difficulty scalar for that level */ public float getDifficultyForLevel(int level) { return mCanonicalDifficultyHistory.get(level); } public void prepLevel() { // Increment the level counter mCurrentLevel++; // Calculate the total number of candies that are going to be dispersed for this level // The still glaring problem here is the level 1 // The floored variant on the current difficulty is used quite often for certain calculations short flooredDifficulty = (short)Math.floor(getDifficultyForLevel(mCurrentLevel)); // Grab the previous candy total before modifying the value mPreviousLevelCandyTotal = mTotalCandiesToDrop; // Adjust the new total of candies for this level mTotalCandiesToDrop = calculateTotalCandies(); /* * Determine the player's tap threshold. This is the number of candies the player must tap on * in order to successfully advance to the next level. * * The equation to calculate this is as follows: * * c * (0.8 + (floor(d) / 100)) * * where * * c = The current number of total candies to drop * d = The difficulty (flooredDifficulty already contains the value to use here) * * Logic * * The threshold needs to be a fraction of the total number of candies that are going to be dropped. * Theoretically, it is possible and logically permissible for the threshold to equal the total to be * dropped but this equation, as a function the confines of the number set defined by the number of playable * levels, doesn't ever cross 100% so yields are never greater than or equal to the current number * of dropped candies. Originally the equation was nothing more than 80% of the total drop count. However, * despite the total drop count being abstractly derived from the logarithmic difficulty curve, the yield * was exponential. In this way, yields appear to be exponential but are more tightly bound to the total * drop count and vary in subtle ways as the series continues on. I have a feeling that the subtle hints * of the exponential curve come about from using the constant of 80% in the calculation. */ mPlayerTapThreshold = (short)(mTotalCandiesToDrop * (0.8F + (flooredDifficulty / 100))); // The player couldn't have tapped any candies at this point, so set the counter to zero mCurrentTappedCandies = 0; // TEST - Instantiate the Candy Drop Pulse mCandyDropPulse = new Pulse(getDifficultyForLevel(mCurrentLevel), 20F, 1.0F, 1000F); } public void resetLevel() { mCurrentLevel--; // Increment the level counter mCurrentLevel++; // Calculate the total number of candies that are going to be dispersed for this level // The still glaring problem here is the level 1 // The floored variant on the current difficulty is used quite often for certain calculations short flooredDifficulty = (short)Math.floor(getDifficultyForLevel(mCurrentLevel)); // Grab the previous candy total before modifying the value // mPreviousLevelCandyTotal = mTotalCandiesToDrop; // Adjust the new total of candies for this level mTotalCandiesToDrop = calculateTotalCandies(); /* * Determine the player's tap threshold. This is the number of candies the player must tap on * in order to successfully advance to the next level. * * The equation to calculate this is as follows: * * c * (0.8 + (floor(d) / 100)) * * where * * c = The current number of total candies to drop * d = The difficulty (flooredDifficulty already contains the value to use here) * * Logic * * The threshold needs to be a fraction of the total number of candies that are going to be dropped. * Theoretically, it is possible and logically permissible for the threshold to equal the total to be * dropped but this equation, as a function the confines of the number set defined by the number of playable * levels, doesn't ever cross 100% so yields are never greater than or equal to the current number * of dropped candies. Originally the equation was nothing more than 80% of the total drop count. However, * despite the total drop count being abstractly derived from the logarithmic difficulty curve, the yield * was exponential. In this way, yields appear to be exponential but are more tightly bound to the total * drop count and vary in subtle ways as the series continues on. I have a feeling that the subtle hints * of the exponential curve come about from using the constant of 80% in the calculation. */ mPlayerTapThreshold = (short)(mTotalCandiesToDrop * (0.8F + (flooredDifficulty / 100))); // The player couldn't have tapped any candies at this point, so set the counter to zero mCurrentTappedCandies = 0; // TEST - Instantiate the Candy Drop Pulse mCandyDropPulse = new Pulse(getDifficultyForLevel(mCurrentLevel), 1.0F, 1.0F, 1000F); } /** * @return True if the player has Continues to use, False otherwise */ public boolean canResetLevel() { if(mContinues > 0) { return true; } else { return false; } } /** * Determine, pseudo-randomly, if the game can drop a candy or not. Ideally, lower levels will drop candies * at a slower rate than later ones. However, a logarithmic curve for drop rate wouldn't alone be sufficient. * * @return If we can drop a candy now or not */ public boolean canDropCandy() { return mCandyDropPulse.isCurrentPointWithinThreshold(); } /** * @return True if the player has tapped the required number of candies to proceed to the next level */ public boolean hasPlayerCrossedTapThreshold() { if(mCurrentTappedCandies > mPlayerTapThreshold) { return true; } else { return false; } } }