2018-03-08 09:24:35 +00:00
|
|
|
|
/****************************************************************************************
|
|
|
|
|
|
|
|
|
|
Algorithm by r57shell & feos, 2018
|
|
|
|
|
|
|
|
|
|
_zeros is the key to GREENZONE DECAY PATTERN.
|
|
|
|
|
|
|
|
|
|
In a 16 element example, we evaluate these bitwise numbers to count zeros on the right.
|
|
|
|
|
First element is always assumed to be 16, which has all 4 bits set to 0. Each right zero
|
|
|
|
|
means that we lower the priority of a state that goes at that index. Priority changes
|
|
|
|
|
depending on current frame and amount of states. States with biggest priority get erased
|
|
|
|
|
first. With a 4-bit battern and no initial gap between states, total frame coverage is
|
|
|
|
|
about 5 times state count. Initial state gap can screw up our patterns, so do all
|
|
|
|
|
calculations like gap isn't there, and take it back into account afterwards.
|
|
|
|
|
|
|
|
|
|
_zeros values are essentialy the values of rshiftby here:
|
|
|
|
|
bitwise view frame rshiftby priority
|
|
|
|
|
00010000 0 4 1
|
|
|
|
|
00000001 1 0 15
|
|
|
|
|
00000010 2 1 7
|
|
|
|
|
00000011 3 0 13
|
|
|
|
|
00000100 4 2 3
|
|
|
|
|
00000101 5 0 11
|
|
|
|
|
00000110 6 1 5
|
|
|
|
|
00000111 7 0 9
|
|
|
|
|
00001000 8 3 1
|
|
|
|
|
00001001 9 0 7
|
|
|
|
|
00001010 10 1 3
|
|
|
|
|
00001011 11 0 5
|
|
|
|
|
00001100 12 2 1
|
|
|
|
|
00001101 13 0 3
|
|
|
|
|
00001110 14 1 1
|
|
|
|
|
00001111 15 0 1
|
|
|
|
|
|
|
|
|
|
*****************************************************************************************/
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Client.Common
|
|
|
|
|
{
|
|
|
|
|
internal class StateManagerDecay
|
|
|
|
|
{
|
|
|
|
|
private TasStateManager _tsm; // access tsm methods to make life easier
|
|
|
|
|
private List<int> _zeros; // amount of least significant zeros in bitwise view (also max pattern step)
|
|
|
|
|
private int _bits; // size of _zeros is 2 raised to the power of _bits
|
|
|
|
|
private int _mask; // for remainder calculation using bitwise instead of division
|
|
|
|
|
private int _base; // repeat count (like fceux's capacity). only used by aligned formula
|
|
|
|
|
private int _capacity; // total amount of savestates
|
|
|
|
|
private int _step; // initial memory state gap
|
|
|
|
|
private bool _align; // extra care about fine alignment. TODO: do we want it?
|
|
|
|
|
|
|
|
|
|
public StateManagerDecay(TasStateManager tsm)
|
|
|
|
|
{
|
|
|
|
|
_tsm = tsm;
|
|
|
|
|
_align = false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-11 14:57:03 +00:00
|
|
|
|
// todo: go through all states once, remove as many as we need. refactor to not need goto
|
2018-03-08 09:24:35 +00:00
|
|
|
|
public void Trigger(int decayStates)
|
|
|
|
|
{
|
|
|
|
|
for (; decayStates > 0 && _tsm.StateCount > 1;)
|
|
|
|
|
{
|
|
|
|
|
int baseStateIndex = _tsm.GetStateIndexByFrame(Global.Emulator.Frame);
|
|
|
|
|
int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step;
|
|
|
|
|
int forwardPriority = -1000000;
|
|
|
|
|
int backwardPriority = -1000000;
|
|
|
|
|
int forwardFrame = -1;
|
|
|
|
|
int backwardFrame = -1;
|
|
|
|
|
|
|
|
|
|
for (int currentStateIndex = 1; currentStateIndex < baseStateIndex; currentStateIndex++)
|
|
|
|
|
{
|
2018-03-11 10:49:18 +00:00
|
|
|
|
int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex);
|
2018-03-08 09:24:35 +00:00
|
|
|
|
|
2018-03-11 10:49:18 +00:00
|
|
|
|
if (_tsm.StateIsMarker(currentFrame))
|
2018-03-08 09:24:35 +00:00
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-03-11 14:57:03 +00:00
|
|
|
|
else if (currentFrame % _step > 0)
|
|
|
|
|
{
|
|
|
|
|
// ignore the pattern if the state doesn't belong already, drop it blindly and skip everything
|
|
|
|
|
_tsm.RemoveState(currentFrame);
|
|
|
|
|
decayStates--;
|
|
|
|
|
|
|
|
|
|
// this is the kind of highly complex loops that might justify goto
|
|
|
|
|
goto next_state;
|
|
|
|
|
}
|
2018-03-11 10:49:18 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
currentFrame /= _step;
|
|
|
|
|
}
|
2018-03-08 09:24:35 +00:00
|
|
|
|
|
|
|
|
|
int zeroCount = _zeros[currentFrame & _mask];
|
|
|
|
|
int priority = ((baseStateFrame - currentFrame) >> zeroCount);
|
|
|
|
|
|
|
|
|
|
if (_align)
|
|
|
|
|
{
|
|
|
|
|
priority -= ((_base * ((1 << zeroCount) * 2 - 1)) >> zeroCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (priority > forwardPriority)
|
|
|
|
|
{
|
|
|
|
|
forwardPriority = priority;
|
|
|
|
|
forwardFrame = currentFrame;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int currentStateIndex = _tsm.StateCount - 1; currentStateIndex > baseStateIndex; currentStateIndex--)
|
|
|
|
|
{
|
|
|
|
|
int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex) / _step;
|
|
|
|
|
|
2018-03-11 14:57:03 +00:00
|
|
|
|
if (_tsm.StateIsMarker(currentFrame))
|
2018-03-08 09:24:35 +00:00
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-03-11 14:57:03 +00:00
|
|
|
|
else if (currentFrame % _step > 0)
|
|
|
|
|
{
|
|
|
|
|
// ignore the pattern if the state doesn't belong already, drop it blindly and skip everything
|
|
|
|
|
_tsm.RemoveState(currentFrame);
|
|
|
|
|
decayStates--;
|
|
|
|
|
|
|
|
|
|
// this is the kind of highly complex loops that might justify goto
|
|
|
|
|
goto next_state;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
currentFrame /= _step;
|
|
|
|
|
}
|
2018-03-08 09:24:35 +00:00
|
|
|
|
|
|
|
|
|
int zeroCount = _zeros[currentFrame & _mask];
|
|
|
|
|
int priority = ((currentFrame - baseStateFrame) >> zeroCount);
|
|
|
|
|
|
|
|
|
|
if (_align)
|
|
|
|
|
{
|
|
|
|
|
priority -= ((_base * ((1 << zeroCount) * 2 - 1)) >> zeroCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (priority > backwardPriority)
|
|
|
|
|
{
|
|
|
|
|
backwardPriority = priority;
|
|
|
|
|
backwardFrame = currentFrame;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (forwardFrame > -1 && backwardFrame > -1)
|
|
|
|
|
{
|
|
|
|
|
if (baseStateFrame - forwardFrame > backwardFrame - baseStateFrame)
|
|
|
|
|
{
|
|
|
|
|
_tsm.RemoveState(forwardFrame * _step);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_tsm.RemoveState(backwardFrame * _step);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decayStates--;
|
|
|
|
|
}
|
|
|
|
|
else if (forwardFrame > -1)
|
|
|
|
|
{
|
|
|
|
|
_tsm.RemoveState(forwardFrame * _step);
|
|
|
|
|
decayStates--;
|
|
|
|
|
}
|
|
|
|
|
else if (backwardFrame > -1)
|
|
|
|
|
{
|
|
|
|
|
_tsm.RemoveState(backwardFrame * _step);
|
|
|
|
|
decayStates--;
|
|
|
|
|
}
|
2018-03-11 14:57:03 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// we're very sorry about failing to find states to remove, but we can't go beyond capacity, so remove at least something
|
|
|
|
|
// this shouldn't happen, but if we don't do it here, nothing good will happen either
|
|
|
|
|
_tsm.RemoveState(_tsm.GetStateFrameByIndex(1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this is the kind of highly complex loops that might justify goto
|
|
|
|
|
next_state:;
|
2018-03-08 09:24:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UpdateSettings(int capacity, int step, int bits)
|
|
|
|
|
{
|
|
|
|
|
_capacity = capacity;
|
|
|
|
|
_step = step;
|
|
|
|
|
_bits = bits;
|
|
|
|
|
_mask = (1 << _bits) - 1;
|
|
|
|
|
_base = (_capacity + _bits / 2) / (_bits + 1);
|
|
|
|
|
_zeros = new List<int>();
|
|
|
|
|
_zeros.Add(_bits);
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < (1 << _bits); i++)
|
|
|
|
|
{
|
|
|
|
|
_zeros.Add(0);
|
|
|
|
|
|
|
|
|
|
for (int j = i; j > 0; j >>= 1)
|
|
|
|
|
{
|
|
|
|
|
if ((j & 1) > 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_zeros[i]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|