feat(replay): add replay engine
This commit is contained in:
1 parent
3eaf1cb369
commit
de89553bb0
1 file changed
+119
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using Chip8.Core;
|
||||
|
||||
namespace Chip8.Replay;
|
||||
|
||||
public enum ReplayMode
|
||||
{
|
||||
Record,
|
||||
Playback,
|
||||
}
|
||||
|
||||
public sealed class ReplayEngine
|
||||
{
|
||||
public Machine machine { get; }
|
||||
public InputMovie movie { get; }
|
||||
public ReplayMode mode { get; }
|
||||
public int instructionsPerFrame { get; }
|
||||
public int frameIndex { get; private set; }
|
||||
|
||||
ReplayEngine(Machine machine, InputMovie movie, ReplayMode mode)
|
||||
{
|
||||
this.machine = machine;
|
||||
this.movie = movie;
|
||||
this.mode = mode;
|
||||
instructionsPerFrame = movie.instructionsPerFrame;
|
||||
}
|
||||
|
||||
public static ReplayEngine record(Machine machine, InputMovie movie) =>
|
||||
new ReplayEngine(machine, movie, ReplayMode.Record);
|
||||
|
||||
public static ReplayEngine playback(InputMovie movie, byte[] rom) =>
|
||||
new ReplayEngine(MachineBuilder.fromMovie(movie, rom), movie, ReplayMode.Playback);
|
||||
|
||||
public static ReplayEngine playback(InputMovie movie, Machine machine) =>
|
||||
new ReplayEngine(machine, movie, ReplayMode.Playback);
|
||||
|
||||
public bool finished => frameIndex >= movie.length || machine.exited;
|
||||
|
||||
public void pressKey(int key)
|
||||
{
|
||||
requireRecord();
|
||||
machine.pressKey(key);
|
||||
}
|
||||
|
||||
public void releaseKey(int key)
|
||||
{
|
||||
requireRecord();
|
||||
machine.releaseKey(key);
|
||||
}
|
||||
|
||||
public void setKeys(KeyMask keys)
|
||||
{
|
||||
requireRecord();
|
||||
keys.applyTo(machine.keypad);
|
||||
}
|
||||
|
||||
public void advanceFrame()
|
||||
{
|
||||
if (mode == ReplayMode.Record)
|
||||
{
|
||||
KeyMask observed = KeyMask.fromKeypad(machine.keypad);
|
||||
movie.recordFrame(frameIndex, observed);
|
||||
machine.runFrame(instructionsPerFrame);
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyMask injected = movie.keysAt(frameIndex);
|
||||
injected.applyTo(machine.keypad);
|
||||
machine.runFrame(instructionsPerFrame);
|
||||
}
|
||||
|
||||
frameIndex++;
|
||||
|
||||
if (mode == ReplayMode.Record)
|
||||
{
|
||||
movie.extendLength(frameIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void advanceFrames(int count)
|
||||
{
|
||||
for (int frame = 0; frame < count; frame++)
|
||||
{
|
||||
advanceFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public void playToEnd()
|
||||
{
|
||||
while (!finished)
|
||||
{
|
||||
advanceFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public ReplayOutcome outcome() => ReplayOutcome.capture(machine, frameIndex);
|
||||
|
||||
public static ReplayOutcome replayToEnd(InputMovie movie, byte[] rom)
|
||||
{
|
||||
var engine = playback(movie, rom);
|
||||
engine.playToEnd();
|
||||
return engine.outcome();
|
||||
}
|
||||
|
||||
public static bool isDeterministic(InputMovie movie, byte[] rom)
|
||||
{
|
||||
ReplayOutcome first = replayToEnd(movie, rom);
|
||||
ReplayOutcome second = replayToEnd(movie, rom);
|
||||
return first.sameAs(second);
|
||||
}
|
||||
|
||||
void requireRecord()
|
||||
{
|
||||
if (mode != ReplayMode.Record)
|
||||
{
|
||||
throw new InvalidOperationException("input can only be supplied while recording");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user