User:ChouxZi3

From Ukikipedia
Jump to: navigation, search

public static class TtcMain { public static void FindIdealReentryManipulation() { TtcSaveState saveState = new TtcSaveState(); int startingFrame = MupenUtilities.GetFrameCount(); List<List<int>> dustFramesLists = GetDustFrameLists(startingFrame + 2, 25, 25);

Config.Print("START FindIdealReentryManipulation"); foreach (List<int> dustFrames in dustFramesLists) { TtcSimulation simulation = new TtcSimulation(saveState, startingFrame, dustFrames); simulation.FindIdealReentryManipulationGivenDustFrames(dustFrames); } Config.Print("END FindIdealReentryManipulation"); } }

public class TtcSimulation { private readonly TtcRng _rng; private readonly List<TtcObject> _rngObjects; private readonly int _startingFrame; private int _currentFrame;

public TtcSimulation(TtcSaveState saveState, int startingFrame, List<int> dustFrames) { (_rng, _rngObjects) = TtcUtilities.CreateRngObjectsFromSaveState(saveState); _startingFrame = startingFrame; _currentFrame = _startingFrame; AddDustFrames(dustFrames); }

public TtcSaveState GetSaveState() { return new TtcSaveState(_rng.GetRng(), _rngObjects); }

// Given dust, goes forward and spawns height swings to investigate public void FindIdealReentryManipulationGivenDustFrames(List<int> dustFrames) { int phase1Limit = 1000;

int maxDustFrame = dustFrames.Count == 0 ? 0 : dustFrames.Max(); int counter = 0; int frame = _startingFrame; while (frame < _startingFrame + phase1Limit) { counter++; frame++; foreach (TtcObject rngObject in _rngObjects) { rngObject.SetFrame(frame); rngObject.Update(); }

// Check if pendulum will do height swing after all dust has been made TtcPendulum pendulum = GetReentryPendulum(); if (frame > maxDustFrame && pendulum._accelerationDirection == -1 && pendulum._accelerationMagnitude == 13 && pendulum._angularVelocity == 0 && pendulum._waitingTimer == 0 && pendulum._angle == 42748) { TtcSimulation simulation = new TtcSimulation(GetSaveState(), frame, new List<int>()); simulation.FindIdealReentryManipulationGivenFrame1(dustFrames, frame); } } }

// Given frame 1, goes forward and spawns wall push swings to investigate // Frame 1 is the frame at the start of the pendulum swing that lets Mario get the right height public void FindIdealReentryManipulationGivenFrame1(List<int> dustFrames, int frame1) { //Config.Print("TRY\t{0}\t{1}", frame1, "[" + string.Join(",", dustFrames) + "]"); int phase2Limit = 1000;

TtcPendulum pendulum = GetReentryPendulum(); TtcBobomb firstBobomb = GetFirstBobomb(); TtcBobomb secondBobomb = GetSecondBobomb(); TtcBobomb thirdBobomb = null; TtcBobomb fourthBobomb = null;

int counter = 0; int frame = _startingFrame; while (frame < _startingFrame + phase2Limit) { counter++; frame++; foreach (TtcObject rngObject in _rngObjects) { // coin for bobomb 1 if (counter == 162 && rngObject == firstBobomb) { _rng.PollRNG(3); } // coin for bobomb 2 if (counter == 258 && rngObject == secondBobomb) { _rng.PollRNG(3); } rngObject.SetFrame(frame); rngObject.Update(); }

// bob-omb 2 start if (counter == 19) { secondBobomb.SetWithinMarioRange(1); }

// bob-omb 2 end, bob-omb 4 start if (counter == 258) { _rngObjects.Remove(secondBobomb); fourthBobomb = new TtcBobomb(_rng, 0, 0); // starts outside range _rngObjects.Insert(68, fourthBobomb); }

// bob-omb 1 start if (counter == 154) { firstBobomb.SetWithinMarioRange(1); }

// bob-omb 1 end, bob-omb 3 start if (counter == 162) { _rngObjects.Remove(firstBobomb); thirdBobomb = new TtcBobomb(_rng, 0, 1); // starts inside range _rngObjects.Insert(68, thirdBobomb); }

// bob-omb 3 exiting range if (counter == 363) { thirdBobomb.SetWithinMarioRange(0); }

// dust frames if (counter >= 84 && counter <= 95 && counter != 93) { _rng.PollRNG(4); }

// bob-omb 2 fuse smoke if ((counter >= 99 && counter <= 211 && counter % 8 == 3) || (counter >= 219 && counter <= 257 && counter % 2 == 1)) { _rng.PollRNG(3); }

// bob-omb 1 fuse smoke if (counter >= 156 && counter <= 162 && counter % 2 == 0) { _rng.PollRNG(3); }

// pendulum must have enough waiting frames if (counter == 162) { bool pendulumQualifies = pendulum._waitingTimer >= 18; if (!pendulumQualifies) return; }

// Check if pendulum will do wall push swing if (counter > 363 + 15 && pendulum._accelerationDirection == -1 && pendulum._accelerationMagnitude == 42 && pendulum._angularVelocity == 0 && pendulum._waitingTimer == 0 && pendulum._angle == 42748) { TtcSimulation simulation = new TtcSimulation(GetSaveState(), frame, new List<int>()); simulation.FindIdealReentryManipulationGivenFrame2(dustFrames, frame1, frame); }

//Config.Print(frame + "\t" + _rng.GetIndex() + "\t" + GetSaveState()); } }

// Investigates a wall push swing to see if it qualifies // Frame 2 is the frame at the start of the pendulum swing that lets Mario get wall displacement public void FindIdealReentryManipulationGivenFrame2(List<int> dustFrames, int frame1, int frame2) { //Config.Print("ATTEMPT\t{0}\t{1}\t{2}", frame1, frame2, "[" + string.Join(",", dustFrames) + "]"); int counter = 0; int frame = _startingFrame; while (true) { counter++; frame++; foreach (TtcObject rngObject in _rngObjects) { rngObject.SetFrame(frame); rngObject.Update(); }

// bob-omb 1 is in range if (counter == 63) { GetFirstBobomb().SetWithinMarioRange(1); }

// collecting star particles if (counter == 66) { _rng.PollRNG(80); }

// bob-omb 2 is in range if (counter == 70) { GetSecondBobomb().SetWithinMarioRange(1); }

// hand is in position if (counter == 77) { TtcHand hand = GetLowerHand(); int min = 36700; int max = 39400; bool handQualifies = hand._angle >= min && hand._angle <= max; if (!handQualifies) return; }

// spinner is in position if (counter == 122) { TtcSpinner spinner = GetLowestSpinner(); int min = 12600; int max = 14700; bool spinnerAngleQualifies = (spinner._angle >= min && spinner._angle <= max) || (spinner._angle >= min + 32768 && spinner._angle <= max + 32768); bool spinnerDirectionQualifies = spinner._direction == -1; bool spinnerQualifies = spinnerAngleQualifies && spinnerDirectionQualifies; if (!spinnerQualifies) return;

List<int> inputDustFrames = dustFrames.ConvertAll(dustFrame => dustFrame - 2); Config.Print("SUCCESS\t{0}\t{1}\t{2}\t", frame1, frame2, "[" + string.Join(",", inputDustFrames) + "]"); return; } } } }

public class PendulumSwingTable { public struct PendulumSwingReference { public int Amplitude; public int Index;

public override int GetHashCode() { return Amplitude; } }

Dictionary<int, PendulumSwingReference> _amplitudeDictionary = new Dictionary<int, PendulumSwingReference>(); Dictionary<int, PendulumSwingReference> _indexDictionary = new Dictionary<int, PendulumSwingReference>();

Dictionary<int, string> _extendedAmplitudeDictionary = new Dictionary<int, string>();

public PendulumSwingTable() { }

public void Add(PendulumSwingReference pendulumSwingRef) { _amplitudeDictionary.Add(pendulumSwingRef.Amplitude, pendulumSwingRef); _indexDictionary.Add(pendulumSwingRef.Index, pendulumSwingRef);

_extendedAmplitudeDictionary.Add(pendulumSwingRef.Amplitude, pendulumSwingRef.Index.ToString()); }

public int? GetPendulumSwingIndex(int amplitude) { if (_amplitudeDictionary.ContainsKey(amplitude)) return _amplitudeDictionary[amplitude].Index;

// Short circuit this case, otherwise Math.Abs throws an exception if (amplitude == Int32.MinValue) return null;

// Check for pendulum swings beyond the standard indexes int absAmplitude = Math.Abs(amplitude); int tenativeFrames = (int)((-21 + Math.Sqrt(441 + 84 * absAmplitude)) / 42); int tentativeAmplitude = tenativeFrames * (tenativeFrames + 1) * 21; if (absAmplitude == tentativeAmplitude && absAmplitude > 7182) { if ((amplitude > 0) == (tenativeFrames % 2 == 0)) // beyond forward indexes { return tenativeFrames + 270; } else // beyond backward indexes { return -1 * tenativeFrames - 363; } }

return null; }

public int GetPendulumAmplitude(int index) { if (_indexDictionary.ContainsKey(index)) return _indexDictionary[index].Amplitude;

int beyondIndex = index > 288 ? index - 288 : -381 - index; int amplitudeMagnitude = 7182 + 777 * beyondIndex + 21 * (beyondIndex * beyondIndex); int sign = index % 2 == 0 ? 1 : -1; return amplitudeMagnitude * sign; }

public string GetPendulumSwingIndexExtended(int amplitude) { int? pendulumIndex = GetPendulumSwingIndex(amplitude); if (pendulumIndex.HasValue) return pendulumIndex.Value.ToString();

if (_extendedAmplitudeDictionary.ContainsKey(amplitude)) return _extendedAmplitudeDictionary[amplitude];

return Double.NaN.ToString(); }

public (int, int)? GetPendulumSwingIndexExtendedPair(int amplitude) { string index = GetPendulumSwingIndexExtended(amplitude); if (index == Double.NaN.ToString()) return null; int hyphenIndex = index.LastIndexOf('-');

int? primaryIndex, secondaryIndex; if (hyphenIndex == -1 || hyphenIndex == 0) { primaryIndex = ParsingUtilities.ParseIntNullable(index); secondaryIndex = 0; } else { primaryIndex = ParsingUtilities.ParseIntNullable(index.Substring(0, hyphenIndex)); secondaryIndex = ParsingUtilities.ParseIntNullable(index.Substring(hyphenIndex + 1)); }

if (primaryIndex == null || secondaryIndex == null) return null; return (primaryIndex.Value, secondaryIndex.Value); }

public void FillInExtended() { int range = 100; // 2000;

List<int> startingIndexes = new List<int>(); for (int i = 0; i < range; i++) { startingIndexes.Add(289 + i); } for (int i = 0; i < range; i++) { startingIndexes.Add(-382 - i); }

List<PendulumSwing> startingSwings = startingIndexes.ConvertAll( index => new PendulumSwing(GetPendulumAmplitude(index), 0, null, index, 0)); Queue<PendulumSwing> queue = new Queue<PendulumSwing>(); foreach (PendulumSwing swing in startingSwings) { List<PendulumSwing> successors = swing.GetSuccessors(); successors.ForEach(successor => queue.Enqueue(successor)); }

while (queue.Count > 0) { PendulumSwing dequeue = queue.Dequeue(); if (GetPendulumSwingIndexExtended(dequeue.Amplitude) != Double.NaN.ToString()) continue; if (dequeue.SecondaryIndex > range) continue;

string extendedIndex = dequeue.PrimaryIndex + "-" + dequeue.SecondaryIndex; _extendedAmplitudeDictionary[dequeue.Amplitude] = extendedIndex;

List<PendulumSwing> successors = dequeue.GetSuccessors(); successors.ForEach(successor => queue.Enqueue(successor)); } }

public class PendulumSwing { public readonly int Amplitude; public readonly int Acceleration; public readonly PendulumSwing Predecessor; public readonly int PrimaryIndex; public readonly int SecondaryIndex;

public PendulumSwing( int amplitude, int acceleration, PendulumSwing predecessor, int primaryIndex, int secondaryIndex) { Amplitude = amplitude; Acceleration = acceleration; Predecessor = predecessor; PrimaryIndex = primaryIndex; SecondaryIndex = secondaryIndex; }

public List<PendulumSwing> GetSuccessors() { return new List<PendulumSwing>() { new PendulumSwing((int)WatchVariableSpecialUtilities.GetPendulumAmplitude(Amplitude, 13), 13, this, PrimaryIndex, SecondaryIndex + 1), new PendulumSwing((int)WatchVariableSpecialUtilities.GetPendulumAmplitude(Amplitude, 42), 42, this, PrimaryIndex, SecondaryIndex + 1), }; }

public override string ToString() { string predecessorString = Predecessor?.ToString() ?? ""; return predecessorString + " =>" + Acceleration + "=> " + Amplitude; } } }

public static class CalculatorMain { public static void CalculateMovementForTtmHolp() { float startX = 1094.12268066406f; float startY = -476.171997070313f; float startZ = -3675.9716796875f; float startXSpeed = -6.70571994781494f; float startYSpeed = -52f; float startZSpeed = -0.628647029399872f; float startHSpeed = -6.70173645019531f;

ushort marioAngle = 16455;

Dictionary<int, ushort> cameraAngles = new Dictionary<int, ushort>() { [0] = 28563, [1] = 28552, [2] = 28548, [3] = 28533, [4] = 28524, [5] = 28514, [6] = 28500, };

float goalX = 1060.860229f; float goalY = -5001.017029f; float goalZ = -3678.57666f;

int xInput = 56; int zInput = 22; int xRadius = 5; int zRadius = 5;

MarioState startState = new MarioState( startX, startY, startZ, startXSpeed, startYSpeed, startZSpeed, startHSpeed, marioAngle, cameraAngles[0], null, null, 0);

int lastIndex = -1; List<Input> inputs = CalculatorUtilities.GetInputRange(xInput - xRadius, xInput + xRadius, zInput - zRadius, zInput + zRadius); float bestDiff = float.MaxValue; MarioState bestState = null; Queue<MarioState> queue = new Queue<MarioState>(); HashSet<MarioState> alreadySeen = new HashSet<MarioState>(); queue.Enqueue(startState); alreadySeen.Add(startState);

while (queue.Count != 0) { MarioState dequeue = queue.Dequeue(); List<MarioState> nextStates = inputs.ConvertAll(input => AirMovementCalculator.ApplyInput(dequeue, input)); nextStates = nextStates.ConvertAll(state => state.WithCameraAngle(cameraAngles[state.Index])); foreach (MarioState state in nextStates) { if (alreadySeen.Contains(state)) continue; if (state.Index > 4) continue;

if (state.Index != lastIndex) { lastIndex = state.Index; System.Diagnostics.Trace.WriteLine("Now at index " + lastIndex); }

if (state.Index == 4) { float diff = (float)MoreMath.GetDistanceBetween(state.X, state.Z, goalX, goalZ);

if (diff > 1 ? diff < bestDiff * 0.5 : diff < bestDiff) { bestDiff = diff; bestState = state; System.Diagnostics.Trace.WriteLine("Diff of " + bestDiff + " is: " + bestState.GetLineage()); } }

alreadySeen.Add(state); queue.Enqueue(state); } } System.Diagnostics.Trace.WriteLine("Done"); }

public static List<(float, float)> GetSuccessFloatPositions() { // initial float startX = -1378.91674804688f; float startY = -2434f; float startZ = -1423.35168457031f; float startXSpeed = 0f; float startYSpeed = 20f; float startZSpeed = 0f; float startHSpeed = 0f;

// after all 4 q steps (no wall displacement) float endX = -1376.13940429688f; float endY = -2414f; float endZ = -1423.66223144531f; float endXSpeed = 2.7774920463562f; float endYSpeed = 16f; float endZSpeed = -0.310500144958496f; float endHSpeed = -1.45670866966248f;

// after 1 q step (no wall displacement) float qstepX = -1378.22241210938f; float qstepY = -2429f; float qstepZ = -1423.42932128906f; float qstepXSpeed = 2.7774920463562f; float qstepYSpeed = -4f; float qstepZSpeed = -0.310500144958496f; float qstepHSpeed = -1.45670866966248f;

// after 1 q step and wall displacement float displacedX = -1307.73107910156f; float displacedY = -2429f; float displacedZ = -1353.11071777344f; float displacedXSpeed = 0f; float displacedYSpeed = -4f; float displacedZSpeed = 0f; float displacedHSpeed = 0f;

// closest starting position that works float closestX = -1378.91381835938f; float closestY = -2434f; float closestZ = -1423.34875488281f; float closestXSpeed = -3.67686033248901f; float closestYSpeed = 0f; float closestZSpeed = -4.74138116836548f; float closestHSpeed = 6f;

// farthest starting position that is within range (doesn't work) float farthestX = -1379.22241210938f; float farthestY = -2434f; float farthestZ = -1423.65734863281f; float farthestXSpeed = 0f; float farthestYSpeed = 0f; float farthestZSpeed = 0f; float farthestHSpeed = 0f;

ushort marioAngle = 39655; ushort cameraAngle = 7142;

TriangleDataModel tri = new TriangleDataModel(0x8015F910);

List<(float, float)> successPositions = new List<(float, float)>(); int numAttempts = 0; int numSuccesses = 0; for (float lineX = closestX, lineZ = closestZ; lineX >= farthestX; lineX -= 0.0001f, lineZ -= 0.0001f) { List<float> pointXs = new List<float>();

float temp = lineX; pointXs.Add(temp);

temp = lineX; for (int i = 0; i < 10; i++) { temp -= 0.0001f; pointXs.Add(temp); }

temp = lineX; for (int i = 0; i < 10; i++) { temp += 0.0001f; pointXs.Add(temp); }

float pointZ = lineZ; foreach (float pointX in pointXs) { MarioState pointState = new MarioState( pointX, startY, pointZ, startXSpeed, startYSpeed, startZSpeed, startHSpeed, marioAngle, cameraAngle, null, null, 0); Input input = new Input(32, -124); MarioState movedState = AirMovementCalculator.ApplyInput(pointState, input, 1); (float dispX, float dispZ) = WallDisplacementCalculator.HandleWallDisplacement( movedState.X, movedState.Y, movedState.Z, tri, 50, 150); bool match = dispX == displacedX && dispZ == displacedZ;

if (match) { successPositions.Add((pointX, pointZ)); /* Config.Print( "({0},{1}) => ({2},{3}) match={4}", (double)pointX, (double)pointZ, (double)dispX, (double)dispZ, match); */ }

numAttempts++; if (match) numSuccesses++; } }

/* Config.Print("numAttempts = " + numAttempts); Config.Print("numSuccesses = " + numSuccesses); */ successPositions.Sort((a, b) => Math.Sign(a.Item1 - b.Item1)); return successPositions; }

public static void TestWalkingCode() { float startX = -7390.01953125f; float startY = -3153f; float startZ = 3936.21435546875f; float startXSpeed = 7.88103151321411f; float startYSpeed = 0f; float startZSpeed = -15.0203580856323f; float startHSpeed = 16.9623641967773f; ushort startMarioAngle = 27738; ushort startCameraAngle = 0;

MarioState marioState = new MarioState( startX, startY, startZ, startXSpeed, startYSpeed, startZSpeed, startHSpeed, startMarioAngle, startCameraAngle, null, null, 0); Input input = new Input(23, 26);

for (int i = 0; i < 10; i++) { Config.Print(i + ": " + marioState); marioState = GroundMovementCalculator.ApplyInput(marioState, input); } }

public static void TestGetRelativePosition() { float marioX = -1431.61889648438f; float marioY = -4003f; float marioZ = -1318.10009765625f; ushort marioAngle = 53906;

(float x, float y, float z) = ObjectCalculator.GetRelativePosition( marioX, marioY, marioZ, marioAngle, 0, 60, 100);

Config.Print("{0},{1},{2}", (double)x, (double)y, (double)z); }

public static void TestGetObjectDisplacement() { float marioX = -1462.44079589844f; float marioY = -4003f; float marioZ = -1196.89099121094f; float marioRadius = 37;

float bobombX = -1538.07922363281f; float bobombY = -4003f; float bobombZ = -1257.61840820313f; float bobombRadius = 65 * 1.2f;

float padding = -5;

(float x, float z) = ObjectCalculator.GetObjectDisplacement( marioX, marioZ, marioRadius, 0, bobombX, bobombZ, bobombRadius, padding); Config.Print("{0},{1}", (double)x, (double)z); }

public static void TestCombined() { float marioX = -918.42724609375f; float marioY = -2434f; float marioZ = -1730.48791503906f; float marioXSpeed = 1.16657888889313f; float marioYSpeed = 0f; float marioZSpeed = 5.46906852722168f; float marioHSpeed = 5.59210300445557f; ushort marioAngle = 2206; ushort cameraAngle = 4132;

float objX = -897.566040039063f; float objZ = -1632.68811035156f;

int inputX = -14; int inputY = -48;

MarioState marioState = new MarioState( marioX, marioY, marioZ, marioXSpeed, marioYSpeed, marioZSpeed, marioHSpeed, marioAngle, cameraAngle, null, null, 0); Input input = new Input(inputX, inputY);

// walking MarioState afterWalkingTemp = GroundMovementCalculator.ApplyInput(marioState, input); MarioState afterWalking = afterWalkingTemp.WithPosition(marioState.X, marioState.Y, marioState.Z);

// displacement (float afterDisplacementX, float afterDisplacementZ) = ObjectCalculator.GetObjectDisplacement( afterWalking.X, afterWalking.Z, 37, afterWalking.MarioAngle, objX, objZ, 65 * 1.2f, -5);

// relative position (float relX, float relY, float relZ) = ObjectCalculator.GetRelativePosition( afterDisplacementX, afterWalking.Y, afterDisplacementZ, afterWalking.MarioAngle, 0, 60, 100);

MarioState finalState = new MarioState( afterDisplacementX, afterWalking.Y, afterDisplacementZ, afterWalking.XSpeed, afterWalking.YSpeed, afterWalking.ZSpeed, afterWalking.HSpeed, afterWalking.MarioAngle, cameraAngle, null, null, 0); Config.Print(finalState); Config.Print("{0},{1},{2}", (double)relX, (double)relY, (double)relZ); }

public static void TestMovementTowardsSpot() { float startX = -1323.72937011719f; float startY = -2434f; float startZ = -1579.7392578125f; float startXSpeed = 2.64395904541016f; float startYSpeed = 0f; float startZSpeed = -11.6073894500732f; float startHSpeed = 11.9047050476074f; ushort startAngle = 30442; List<ushort> cameraAngles = new List<ushort>() { 7997, 8089, 8185, 8276, 8364, 8454, 8546, 8640, 8704, 8983, 9007, 9007, 9007, 9050, 9138, 9225, 9249, 9249, 9249, 9249, 9249, 9249, 9249, }; int INDEX_START = 0;

float objStartX = -1301.52001953125f; float objStartZ = -1677.24182128906f;

int inputX = 127; int inputY = 87;

MarioState marioState = new MarioState( startX, startY, startZ, startXSpeed, startYSpeed, startZSpeed, startHSpeed, startAngle, cameraAngles[INDEX_START], null, null, 0); MarioBobombState marioBobombState = new MarioBobombState( marioState, objStartX, objStartZ);

Input input = new Input(inputX, inputY);

MarioBobombState prevMarioBobombState = null; for (int i = INDEX_START + 1; i < 9; i++) { ushort nextCameraAngle = cameraAngles[i]; prevMarioBobombState = marioBobombState; marioBobombState = ApplyInputToMarioBobombState(marioBobombState, input, nextCameraAngle); } Config.Print(marioBobombState);

MarioState m = prevMarioBobombState.MarioState; (float holpX, float holpY, float holpZ) = HolpCalculator.GetHolp(58, m.X, m.Y, m.Z, m.MarioAngle);

MarioState m2 = marioBobombState.MarioState; float marioX = m2.X; float marioY = m2.Y; float marioZ = m2.Z; ushort marioAngle = m2.MarioAngle; float marioRadius = 37;

float bobombX = holpX; float bobombY = holpY; float bobombZ = holpZ;

float padding = -5;

for (int i = 1; i <= 4; i++) { if (i == 2) { ushort bobombAngle = m.MarioAngle; float delX = 5 * InGameTrigUtilities.InGameSine(bobombAngle); float delZ = 5 * InGameTrigUtilities.InGameCosine(bobombAngle); bobombX += delX; bobombZ += delZ; } float bobombRadius = 65 * (1f + 0.2f * i); (marioX, marioZ) = ObjectCalculator.GetObjectDisplacement( marioX, marioZ, marioRadius, 0, bobombX, bobombZ, bobombRadius, padding); Config.Print("{0}: ({1},{2})", i, (double)marioX, (double)marioZ); } }

public static bool IsInSortedPositions( List<(float, float)> positions, (float, float) position) { return IsInSortedPositions(positions, position, 0, positions.Count - 1); }

public static bool IsInSortedPositions( List<(float, float)> positions, (float, float) position, int startIndex, int endIndex) { if (startIndex > endIndex) return false;

int midIndex = (startIndex + endIndex) / 2; (float midValue1, float midValue2) = positions[midIndex]; if (position.Item1 > midValue1) { return IsInSortedPositions(positions, position, midIndex + 1, endIndex); } else if (position.Item1 < midValue1) { return IsInSortedPositions(positions, position, startIndex, midIndex - 1); } else { return position.Item2 == midValue2; } }

public static float IsInSortedPositions2( List<(float, float)> positions, (float, float) position) { return IsInSortedPositions2(positions, position, 0, positions.Count - 1); }

public static float IsInSortedPositions2( List<(float, float)> positions, (float, float) position, int startIndex, int endIndex) { if (startIndex > endIndex) return float.MaxValue;

int midIndex = (startIndex + endIndex) / 2; (float midValue1, float midValue2) = positions[midIndex]; if (position.Item1 > midValue1) { return IsInSortedPositions2(positions, position, midIndex + 1, endIndex); } else if (position.Item1 < midValue1) { return IsInSortedPositions2(positions, position, startIndex, midIndex - 1); } else { return Math.Abs(position.Item2 - midValue2); } }

public static List<List<int>> GetAngleDiffsList(int length, int mid, int range) { List<int> angleDiffs = new List<int>(); for (int i = -1 * range; i <= range; i++) { int angleDiff = mid + i * 16; angleDiffs.Add(angleDiff); }

List<List<int>> output = new List<List<int>>(); GetAngleDiffsListRecursion(output, new List<int>(), length, angleDiffs); return output; }

public static void GetAngleDiffsListRecursion( List<List<int>> output, List<int> state, int length, List<int> values) { if (state.Count == length) { List<int> temp = new List<int>(state); output.Add(temp); return; }

foreach (int value in values) { state.Add(value); GetAngleDiffsListRecursion(output, state, length, values); state.RemoveAt(state.Count - 1); } }

public static void TestBruteForceMovingToSpot() { Config.Print("START BRUTE FORCE"); List<(float, float)> successPositions = GetSuccessFloatPositions();

bool boolValue = IsInSortedPositions( successPositions, (-1379.0001f, 0f));

List<List<int>> angleDiffsList = GetAngleDiffsList(7, 96, 5); angleDiffsList.ForEach(list => list.Add(0)); // List<int> angleDiffs = new List<int>() { 89, 92, 96, 91, 88, 90, 92, 2048 };

//float minDiff = float.MaxValue; for (int i = 0; i < angleDiffsList.Count; i++) { List<int> angleDiffs = angleDiffsList[i]; (float x, float z) = MoveIntoSpot(angleDiffs); float diff = IsInSortedPositions2(successPositions, (x, z)); if (diff < 0.0002f) { Config.Print("{0}: [{1}] ({2},{3})", i, (double)diff, (double)x, (double)z); MoveIntoSpot(angleDiffs, true); Config.Print(); } } Config.Print("END BRUTE FORCE"); }

public static (float x, float z) MoveIntoSpot(List<int> angleDiffs, bool print = false) { float startX = -1323.72937011719f; float startY = -2434f; float startZ = -1579.7392578125f; float startXSpeed = 2.64395904541016f; float startYSpeed = 0f; float startZSpeed = -11.6073894500732f; float startHSpeed = 11.9047050476074f; ushort startAngle = 30442;

float objStartX = -1301.52001953125f; float objStartZ = -1677.24182128906f;

MarioState marioState = new MarioState( startX, startY, startZ, startXSpeed, startYSpeed, startZSpeed, startHSpeed, startAngle, 0, null, null, 0); MarioBobombState marioBobombState = new MarioBobombState( marioState, objStartX, objStartZ);

MarioBobombState prevMarioBobombState = null; for (int i = 0; i < 8; i++) { prevMarioBobombState = marioBobombState; marioBobombState = ApplyInputToMarioBobombState(marioBobombState, angleDiffs[i]); if (print) { //Config.Print((43226 + i) + ": " + marioBobombState); Config.Print( "{0} {1} {2} {3} {4}", (43227 + i), (double)marioBobombState.MarioState.X, (double)marioBobombState.MarioState.Y, (double)marioBobombState.MarioState.Z, (double)marioBobombState.MarioState.MarioAngle); } } //Config.Print(marioBobombState);

MarioState m = prevMarioBobombState.MarioState; (float holpX, float holpY, float holpZ) = HolpCalculator.GetHolp(58, m.X, m.Y, m.Z, m.MarioAngle);

MarioState m2 = marioBobombState.MarioState; float marioX = m2.X; float marioY = m2.Y; float marioZ = m2.Z; ushort marioAngle = m2.MarioAngle; float marioRadius = 37;

float bobombX = holpX; float bobombY = holpY; float bobombZ = holpZ;

float padding = -5;

for (int i = 1; i <= 4; i++) { if (i == 2) { ushort bobombAngle = m.MarioAngle; float delX = 5 * InGameTrigUtilities.InGameSine(bobombAngle); float delZ = 5 * InGameTrigUtilities.InGameCosine(bobombAngle); bobombX += delX; bobombZ += delZ; } float bobombRadius = 65 * (1f + 0.2f * i); (marioX, marioZ) = ObjectCalculator.GetObjectDisplacement( marioX, marioZ, marioRadius, 0, bobombX, bobombZ, bobombRadius, padding); //Config.Print("{0}: ({1},{2})", i, (double)marioX, (double)marioZ);

if (print) { if (i == 1) { for (int j = 0; j < 4; j++) { Config.Print( "{0} {1} {2} {3} {4}", 43235 + j, (double)marioX, (double)m2.Y, (double)marioZ, (double)m2.MarioAngle); } } else { Config.Print( "{0} {1} {2} {3} {4}", 43237 + i, (double)marioX, (double)m2.Y, (double)marioZ, (double)m2.MarioAngle); } } }

return (marioX, marioZ); }

public class MarioBobombState { public readonly MarioState MarioState; public readonly float ObjX; public readonly float ObjZ;

public MarioBobombState(MarioState marioState, float objX, float objZ) { MarioState = marioState; ObjX = objX; ObjZ = objZ; }

public override string ToString() { return String.Format("{0} obj=({1},{2})", MarioState, (double)ObjX, (double)ObjZ); } }

public static MarioBobombState ApplyInputToMarioBobombState( MarioBobombState initialState, Input input, ushort nextCameraAngle) { // get vars MarioState marioState = initialState.MarioState; float objX = initialState.ObjX; float objZ = initialState.ObjZ;

// walking MarioState afterWalkingTemp = GroundMovementCalculator.ApplyInput(marioState, input); // doesn't move due to ceiling MarioState afterWalking = afterWalkingTemp.WithPosition(marioState.X, marioState.Y, marioState.Z);

// displacement (float afterDisplacementX, float afterDisplacementZ) = ObjectCalculator.GetObjectDisplacement( afterWalking.X, afterWalking.Z, 37, afterWalking.MarioAngle, objX, objZ, 65 * 1.2f, -5);

// relative position (float relX, float relY, float relZ) = ObjectCalculator.GetRelativePosition( afterDisplacementX, afterWalking.Y, afterDisplacementZ, afterWalking.MarioAngle, 0, 60, 100);

MarioState finalMarioState = new MarioState( afterDisplacementX, afterWalking.Y, afterDisplacementZ, afterWalking.XSpeed, afterWalking.YSpeed, afterWalking.ZSpeed, afterWalking.HSpeed, afterWalking.MarioAngle, nextCameraAngle, null, null, 0); MarioBobombState finalMarioBobombState = new MarioBobombState(finalMarioState, relX, relZ); return finalMarioBobombState; }

public static MarioBobombState ApplyInputToMarioBobombState( MarioBobombState initialState, int angleDiff) { // get vars MarioState marioState = initialState.MarioState; float objX = initialState.ObjX; float objZ = initialState.ObjZ;

// walking MarioState afterWalkingTemp = GroundMovementCalculator.ApplyInput(marioState, angleDiff); // doesn't move due to ceiling MarioState afterWalking = afterWalkingTemp.WithPosition(marioState.X, marioState.Y, marioState.Z);

// displacement (float afterDisplacementX, float afterDisplacementZ) = ObjectCalculator.GetObjectDisplacement( afterWalking.X, afterWalking.Z, 37, afterWalking.MarioAngle, objX, objZ, 65 * 1.2f, -5);

// relative position (float relX, float relY, float relZ) = ObjectCalculator.GetRelativePosition( afterDisplacementX, afterWalking.Y, afterDisplacementZ, afterWalking.MarioAngle, 0, 60, 100);

MarioState finalMarioState = new MarioState( afterDisplacementX, afterWalking.Y, afterDisplacementZ, afterWalking.XSpeed, afterWalking.YSpeed, afterWalking.ZSpeed, afterWalking.HSpeed, afterWalking.MarioAngle, 0, null, null, 0); MarioBobombState finalMarioBobombState = new MarioBobombState(finalMarioState, relX, relZ); return finalMarioBobombState; } }

public static class AirMovementCalculator { public static MarioState ApplyInput(MarioState marioState, Input input, int numQSteps = 4) { MarioState withHSpeed = ComputeAirHSpeed(marioState, input); MarioState moved = AirMove(withHSpeed, numQSteps); MarioState withYSpeed = ComputeAirYSpeed(moved); return withYSpeed; }

public static MarioState AirMove(MarioState initialState, int numQSteps = 4) { float newX = initialState.X; float newY = initialState.Y; float newZ = initialState.Z;

for (int i = 0; i < numQSteps; i++) { newX += initialState.XSpeed / 4; newY += initialState.YSpeed / 4; newZ += initialState.ZSpeed / 4; }

return new MarioState( newX, newY, newZ, initialState.XSpeed, initialState.YSpeed, initialState.ZSpeed, initialState.HSpeed, initialState.MarioAngle, initialState.CameraAngle, initialState.PreviousState, initialState.LastInput, initialState.Index); }

// update_air_without_turn private static MarioState ComputeAirHSpeed(MarioState initialState, Input input) { bool longJump = false; int maxSpeed = longJump ? 48 : 32;

ushort marioAngle = initialState.MarioAngle; ushort yawIntended = MoreMath.CalculateAngleFromInputs(input.X, input.Y, initialState.CameraAngle); int deltaAngleIntendedFacing = yawIntended - marioAngle; float inputScaledMagnitude = input.GetScaledMagnitude();

float perpSpeed = 0; float newHSpeed = ApproachHSpeed(initialState.HSpeed, 0, 0.35f, 0.35f); if (inputScaledMagnitude > 0) { newHSpeed += (inputScaledMagnitude / 32) * 1.5f * InGameTrigUtilities.InGameCosine(deltaAngleIntendedFacing); perpSpeed = InGameTrigUtilities.InGameSine(deltaAngleIntendedFacing) * (inputScaledMagnitude / 32) * 10; }

if (newHSpeed > maxSpeed) newHSpeed -= 1; if (newHSpeed < -16) newHSpeed += 2;

float newSlidingXSpeed = InGameTrigUtilities.InGameSine(marioAngle) * newHSpeed; float newSlidingZSpeed = InGameTrigUtilities.InGameCosine(marioAngle) * newHSpeed; newSlidingXSpeed += perpSpeed * InGameTrigUtilities.InGameSine(marioAngle + 0x4000); newSlidingZSpeed += perpSpeed * InGameTrigUtilities.InGameCosine(marioAngle + 0x4000); float newXSpeed = newSlidingXSpeed; float newZSpeed = newSlidingZSpeed;

return new MarioState( initialState.X, initialState.Y, initialState.Z, newXSpeed, initialState.YSpeed, newZSpeed, newHSpeed, initialState.MarioAngle, initialState.CameraAngle, initialState, input, initialState.Index + 1); }

private static float ComputeAirHSpeed(float initialHSpeed) { int maxSpeed = 32; float newHSpeed = ApproachHSpeed(initialHSpeed, 0, 0.35f, 0.35f); if (newHSpeed > maxSpeed) newHSpeed -= 1; if (newHSpeed < -16) newHSpeed += 2; return newHSpeed; }

public static float ComputePosition(float position, float hSpeed, int frames) { for (int i = 0; i < frames; i++) { hSpeed = ComputeAirHSpeed(hSpeed); position += hSpeed; } return position; }

private static MarioState ComputeAirYSpeed(MarioState initialState) { float newYSpeed = Math.Max(initialState.YSpeed - 4, -75); return new MarioState( initialState.X, initialState.Y, initialState.Z, initialState.XSpeed, newYSpeed, initialState.ZSpeed, initialState.HSpeed, initialState.MarioAngle, initialState.CameraAngle, initialState.PreviousState, initialState.LastInput, initialState.Index); }

private static float ApproachHSpeed(float speed, float maxSpeed, float increase, float decrease) { if (speed < maxSpeed) return Math.Min(maxSpeed, speed + increase); else return Math.Max(maxSpeed, speed - decrease); } }

public static class GroundMovementCalculator { // act_hold_walking public static MarioState ApplyInput(MarioState initialState, Input input) { MutableMarioState mutableMarioState = initialState.GetMutableMarioState(input); mutableMarioState.IntendedMagnitude *= 0.4f; UpdateWalkingSpeed(mutableMarioState); PerformGroundStep(mutableMarioState); MarioState finalState = mutableMarioState.GetMarioState(initialState, input); return finalState; }

public static MarioState ApplyInput(MarioState initialState, int angleDiff) { MutableMarioState mutableMarioState = initialState.GetMutableMarioState(angleDiff); mutableMarioState.IntendedMagnitude *= 0.4f; UpdateWalkingSpeed(mutableMarioState); PerformGroundStep(mutableMarioState); MarioState finalState = mutableMarioState.GetMarioState(initialState, null); return finalState; }

// update_walking_speed private static void UpdateWalkingSpeed(MutableMarioState marioState) { float maxTargetSpeed; float targetSpeed;

bool slowSurface = false; if (slowSurface) maxTargetSpeed = 24.0f; else maxTargetSpeed = 32.0f;

targetSpeed = marioState.IntendedMagnitude < maxTargetSpeed ? marioState.IntendedMagnitude : maxTargetSpeed;

if (marioState.HSpeed <= 0.0f) marioState.HSpeed += 1.1f; else if (marioState.HSpeed <= targetSpeed) marioState.HSpeed += 1.1f - marioState.HSpeed / 43.0f; else marioState.HSpeed -= 1.0f;

if (marioState.HSpeed > 48.0f) marioState.HSpeed = 48.0f;

marioState.MarioAngle = MoreMath.NormalizeAngleUshort( marioState.IntendedAngle - CalculatorUtilities.ApproachInt( MoreMath.NormalizeAngleShort(marioState.IntendedAngle - marioState.MarioAngle), 0, 0x800, 0x800)); ApplySlopeAccel(marioState); }

// apply_slope_accel private static void ApplySlopeAccel(MutableMarioState marioState) { marioState.XSpeed = marioState.HSpeed * InGameTrigUtilities.InGameSine(marioState.MarioAngle); marioState.YSpeed = 0.0f; marioState.ZSpeed = marioState.HSpeed * InGameTrigUtilities.InGameCosine(marioState.MarioAngle); }

// perform_ground_step private static void PerformGroundStep(MutableMarioState marioState) { for (int i = 0; i < 4; i++) { marioState.X = marioState.X + marioState.XSpeed / 4.0f; marioState.Z = marioState.Z + marioState.ZSpeed / 4.0f; } } }

public static class ObjectCalculator { public static (float newMarioX, float newMarioZ) GetObjectDisplacement( float marioX, float marioZ, float marioRadius, ushort marioAngle, float objectX, float objectZ, float objectRadius, float padding) { float minDistance = objectRadius + marioRadius + padding;

float offsetX = marioX - objectX; float offsetZ = marioZ - objectZ; float distance = (float)Math.Sqrt(offsetX * offsetX + offsetZ * offsetZ);

if (distance < minDistance) { short pushAngle; float newMarioX; float newMarioZ;

if (distance == 0.0f) pushAngle = (short)marioAngle; else pushAngle = (short)InGameTrigUtilities.InGameATan(offsetZ, offsetX);

newMarioX = objectX + minDistance * InGameTrigUtilities.InGameSine(pushAngle); newMarioZ = objectZ + minDistance * InGameTrigUtilities.InGameCosine(pushAngle);

return (newMarioX, newMarioZ); } return (marioX, marioZ); }

public static (float objectX, float objectY, float objectZ) GetRelativePosition( float marioX, float marioY, float marioZ, ushort marioAngle, float dleft, float dy, float dforward) { float facingZ = InGameTrigUtilities.InGameCosine(marioAngle); float facingX = InGameTrigUtilities.InGameSine(marioAngle);

float dz = dforward * facingZ - dleft * facingX; float dx = dforward * facingX + dleft * facingZ;

return (marioX + dx, marioY + dy, marioZ + dz); } }

public static class WallDisplacementCalculator { public static (float newMarioX, float newMarioZ) HandleWallDisplacement( float marioX, float marioY, float marioZ, TriangleDataModel surf, float radius, float offsetY) { return HandleWallDisplacement(marioX, marioY, marioZ, new List<TriangleDataModel>() { surf }, radius, offsetY); }

public static (float newMarioX, float newMarioZ) HandleWallDisplacement( float marioX, float marioY, float marioZ, List<TriangleDataModel> surfs, float radius, float offsetY) { float offset; float x = marioX; float y = marioY + offsetY; float z = marioZ; float px, pz; float w1, w2, w3; float y1, y2, y3;

// Max collision radius = 200 if (radius > 200.0f) radius = 200.0f;

foreach (TriangleDataModel surf in surfs) { if (y < surf.YMinMinus5 || y > surf.YMaxPlus5) continue;

offset = surf.NormX * x + surf.NormY * y + surf.NormZ * z + surf.NormOffset;

if (offset < -radius || offset > radius) continue;

px = x; pz = z;

if (surf.XProjection) { w1 = -surf.Z1; w2 = -surf.Z2; w3 = -surf.Z3; y1 = surf.Y1; y2 = surf.Y2; y3 = surf.Y3;

if (surf.NormX > 0.0f) { if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) > 0.0f) continue; if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) > 0.0f) continue; if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) > 0.0f) continue; } else { if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) < 0.0f) continue; if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) < 0.0f) continue; if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) < 0.0f) continue; } } else { w1 = surf.X1; w2 = surf.X2; w3 = surf.X3; y1 = surf.Y1; y2 = surf.Y2; y3 = surf.Y3;

if (surf.NormZ > 0.0f) { if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) > 0.0f) continue; if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) > 0.0f) continue; if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) > 0.0f) continue; } else { if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) < 0.0f) continue; if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) < 0.0f) continue; if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) < 0.0f) continue; } }

marioX += surf.NormX * (radius - offset); marioZ += surf.NormZ * (radius - offset); }

return (marioX, marioZ); } }

public static class HolpCalculator { private static List<(int, double, double, double)> _data = new List<(int, double, double, double)>() { (0,-13.852560043335,82.7928466796875,43.2764892578125), (1,-13.8603839874268,84.1005249023438,43.2064208984375), (2,-13.8159141540527,84.2417602539063,43.1217041015625), (3,-13.8067932128906,84.3388061523438,43.032958984375), (4,-13.8156032562256,84.4673461914063,42.8843994140625), (5,-13.8143367767334,84.3573608398438,42.7357177734375), (6,-13.8485641479492,84.4801635742188,42.5528564453125), (7,-13.913293838501,84.3718872070313,42.3017578125), (8,-13.9324378967285,84.2554931640625,42.12255859375), (9,-14.0344524383545,83.8282470703125,41.887451171875), (10,-14.1320991516113,83.65185546875,41.6539306640625), (11,-14.2047386169434,83.2032470703125,41.481689453125), (12,-14.3437423706055,82.9824829101563,41.230224609375), (13,-14.4278945922852,82.4707641601563,41.1116943359375), (14,-14.6146621704102,81.8866577148438,40.9158935546875), (15,-14.7635688781738,81.2904663085938,40.7923583984375), (16,-14.983922958374,80.6292724609375,40.6275634765625), (17,-15.2353763580322,79.8695678710938,40.525634765625), (18,-15.4751510620117,79.0504760742188,40.5074462890625), (19,-15.7339839935303,78.1624145507813,40.528564453125), (20,-16.0744380950928,77.209716796875,40.4693603515625), (21,-16.350076675415,76.2724609375,40.4874267578125), (22,-16.6590099334717,75.2498779296875,40.524169921875), (23,-16.9654483795166,74.2208862304688,40.5496826171875), (24,-17.286901473999,73.1162719726563,40.60302734375), (25,-17.5732021331787,72.0418090820313,40.662109375), (26,-17.8684062957764,70.9493408203125,40.6968994140625), (27,-18.1436023712158,70.1430053710938,40.7091064453125), (28,-18.4258403778076,69.06982421875,40.7000732421875), (29,-18.6577587127686,68.073974609375,40.6951904296875), (30,-18.8827457427979,66.8560180664063,40.662353515625), (31,-19.0599193572998,65.9683227539063,40.640380859375), (32,-19.2313365936279,65.8605346679688,40.594970703125), (33,-19.3553791046143,66.3665161132813,40.5447998046875), (34,-19.4837818145752,67.1422729492188,40.4661865234375), (35,-19.5892887115479,67.9154052734375,40.43017578125), (36,-19.659029006958,69.2606811523438,40.4078369140625), (37,-19.7250804901123,70.356201171875,40.3907470703125), (38,-19.7577571868896,71.4926147460938,40.40869140625), (39,-19.7571697235107,72.4199829101563,40.462158203125), (40,-19.7728824615479,73.1315307617188,40.45849609375), (41,-19.7376079559326,73.8190307617188,40.5723876953125), (42,-19.7122821807861,74.5830078125,40.6121826171875), (43,-19.6650981903076,75.282958984375,40.739013671875), (44,-19.57346534729,75.8268432617188,40.88525390625), (45,-19.4797077178955,76.1210327148438,41.033203125), (46,-19.3439426422119,76.4605102539063,41.2265625), (47,-19.2333011627197,76.2872314453125,41.37890625), (48,-19.0889110565186,76.1022338867188,41.597900390625), (49,-18.9064617156982,75.9515380859375,41.857421875), (50,-18.7486705780029,75.5679321289063,42.0560302734375), (51,-18.5583438873291,75.1459350585938,42.33203125), (52,-18.3875865936279,74.7825317382813,42.52978515625), (53,-18.1501483917236,74.1698608398438,42.83203125), (54,-17.9086894989014,73.806396484375,43.1324462890625), (55,-17.7250003814697,73.1712646484375,43.34814453125), (56,-17.4643001556396,72.8579711914063,43.6300048828125), (57,-17.1985988616943,72.2935791015625,43.9110107421875), (58,-16.9619617462158,71.688720703125,44.1668701171875), (59,-16.7159366607666,71.3931274414063,44.3900146484375), (60,-16.4704761505127,70.817626953125,44.6248779296875), (61,-16.1780300140381,70.2903442382813,44.8857421875), (62,-15.9158897399902,70.0347900390625,45.0811767578125), (63,-15.6951370239258,69.4375,45.2755126953125), (64,-15.4251079559326,69.2108764648438,45.447509765625), (65,-15.1985931396484,68.4242553710938,45.596435546875), (66,-14.9584121704102,67.7183227539063,45.701416015625), (67,-14.7734146118164,67.4125366210938,45.7999267578125), (68,-14.5395526885986,68.2246704101563,45.871826171875), (69,-14.385705947876,70.1324462890625,45.7674560546875), (70,-14.2014904022217,72.6931762695313,45.574951171875), (71,-14.1282138824463,75.119140625,45.1312255859375), (72,-13.9819869995117,77.0831298828125,44.716552734375), (73,-13.9631118774414,78.2352905273438,44.156494140625), (74,-13.8647117614746,79.316162109375,43.736083984375), (75,-13.8919486999512,80.1707763671875,43.3465576171875), };

private static Dictionary<int, (double, double, double)> _dictionary;

static HolpCalculator() { _dictionary = new Dictionary<int, (double, double, double)>(); foreach ((int index, double x, double y, double z) in _data) { _dictionary[index] = (x, y, z); } }

public static (float x, float y, float z) GetHolp(int index) { if (!_dictionary.ContainsKey(index)) return (float.NaN, float.NaN, float.NaN); (double xOffset, double yOffset, double zOffset) = _dictionary[index]; return ((float)xOffset, (float)yOffset, (float)zOffset); }

public static (float x, float y, float z) GetHolp( int index, float marioX, float marioY, float marioZ, ushort marioAngle) { if (!_dictionary.ContainsKey(index)) return (float.NaN, float.NaN, float.NaN); (double xOffset, double yOffset, double zOffset) = _dictionary[index];

double vectorMagnitude = MoreMath.GetHypotenuse(xOffset, zOffset); double vectorAngle = MoreMath.AngleTo_AngleUnits(xOffset, zOffset); double rotatedAngle = vectorAngle + MoreMath.NormalizeAngleTruncated(marioAngle); (double rotatedX, double rotatedZ) = MoreMath.GetComponentsFromVector(vectorMagnitude, rotatedAngle);

double offsetX = rotatedX + marioX; double offsetY = yOffset + marioY; double offsetZ = rotatedZ + marioZ;

return ((float)offsetX, (float)offsetY, (float)offsetZ); } }

public static class CalculatorUtilities { public static List<Input> GetAllInputs() { return GetInputRange(-128, 127, -128, 127); }

public static List<Input> GetInputRange(int minX, int maxX, int minZ, int maxZ) { List<Input> output = new List<Input>(); for (int x = minX; x <= maxX; x++) { if (MoreMath.InputIsInDeadZone(x)) continue; for (int z = minZ; z <= maxZ; z++) { if (MoreMath.InputIsInDeadZone(z)) continue; output.Add(new Input(x, z)); } } return output; }

public static int ApproachInt(int current, int target, int inc, int dec) { if (current < target) { current += inc; if (current > target) current = target; } else { current -= dec; if (current < target) current = target; } return current; } }

public class MarioState { public readonly float X; public readonly float Y; public readonly float Z; public readonly float XSpeed; public readonly float YSpeed; public readonly float ZSpeed; public readonly float HSpeed; public readonly ushort MarioAngle; public readonly ushort CameraAngle;

public readonly MarioState PreviousState; public readonly Input LastInput; public readonly int Index;

public MarioState( float x, float y, float z, float xSpeed, float ySpeed, float zSpeed, float hSpeed, ushort marioAngle, ushort cameraAngle, MarioState previousState, Input lastInput, int index) { X = x; Y = y; Z = z; XSpeed = xSpeed; YSpeed = ySpeed; ZSpeed = zSpeed; HSpeed = hSpeed; MarioAngle = marioAngle; CameraAngle = cameraAngle;

PreviousState = previousState; LastInput = lastInput; Index = index; }

public MutableMarioState GetMutableMarioState(Input input) { return new MutableMarioState( X, Y, Z, XSpeed, YSpeed, ZSpeed, HSpeed, MarioAngle, CameraAngle, input); }

public MutableMarioState GetMutableMarioState(int angleDiff) { return new MutableMarioState( X, Y, Z, XSpeed, YSpeed, ZSpeed, HSpeed, MarioAngle, angleDiff); }

public override string ToString() { return String.Format( "pos=({0},{1},{2}) spd=({3},{4},{5}) hspd={6} angle={7}", (double)X, (double)Y, (double)Z, (double)XSpeed, (double)YSpeed, (double)ZSpeed, (double)HSpeed, MarioAngle); }

public string ToStringWithInput() { string inputString = LastInput != null ? LastInput + " to " : ""; return inputString + ToString(); }

private List<object> GetFields() { return new List<object>() { X, Y, Z, XSpeed, YSpeed, ZSpeed, HSpeed, MarioAngle, CameraAngle, }; }

public override bool Equals(object obj) { if (!(obj is MarioState)) return false; MarioState other = obj as MarioState; return Enumerable.SequenceEqual( GetFields(), other.GetFields()); }

public override int GetHashCode() { return GetFields().GetHashCode(); }

public string GetLineage() { if (PreviousState == null) { return ToStringWithInput(); } else { return PreviousState.GetLineage() + "\r\n" + ToStringWithInput(); } }

public MarioState WithCameraAngle(ushort cameraAngle) { return new MarioState( X, Y, Z, XSpeed, YSpeed, ZSpeed, HSpeed, MarioAngle, cameraAngle, PreviousState, LastInput, Index); }

public MarioState WithPosition(float x, float y, float z) { return new MarioState( x, y, z, XSpeed, YSpeed, ZSpeed, HSpeed, MarioAngle, CameraAngle, PreviousState, LastInput, Index); } }

public class MutableMarioState { public float X; public float Y; public float Z; public float XSpeed; public float YSpeed; public float ZSpeed; public float HSpeed; public ushort MarioAngle; public ushort IntendedAngle; public float IntendedMagnitude;

public MutableMarioState( float x, float y, float z, float xSpeed, float ySpeed, float zSpeed, float hSpeed, ushort marioAngle, ushort cameraAngle, Input input) { X = x; Y = y; Z = z; XSpeed = xSpeed; YSpeed = ySpeed; ZSpeed = zSpeed; HSpeed = hSpeed; MarioAngle = marioAngle; IntendedAngle = MoreMath.CalculateAngleFromInputs(input.X, input.Y, cameraAngle); IntendedMagnitude = input.GetScaledMagnitude(); }

public MutableMarioState( float x, float y, float z, float xSpeed, float ySpeed, float zSpeed, float hSpeed, ushort marioAngle, int angleDiff) { X = x; Y = y; Z = z; XSpeed = xSpeed; YSpeed = ySpeed; ZSpeed = zSpeed; HSpeed = hSpeed; MarioAngle = marioAngle; IntendedAngle = MoreMath.NormalizeAngleUshort(marioAngle + angleDiff); IntendedMagnitude = 32; }

public MarioState GetMarioState(MarioState previousState, Input lastInput) { return new MarioState( X, Y, Z, XSpeed, YSpeed, ZSpeed, HSpeed, MarioAngle, previousState.CameraAngle, previousState, lastInput, previousState.Index + 1); } }

// Y value is inputted and stored in sm64 convention // Y value is displayed in mupen convention public class Input { public readonly int X; public readonly int Y;

public Input(int x, int y) { X = x; Y = y; }

public float GetScaledMagnitude() { return MoreMath.GetScaledInputMagnitude(X, Y, false); }

public override string ToString() { return String.Format("({0},{1})", X, -1 * Y); } }