Skip to content

jmhossler/synacor_java

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Synacor Java VM: Quick Start

About

The Synacor challenge was made by Eric Wastl, the creator of Advent of Code,, but unfortunately the site the challenge was hosted on is no longer available. This challenge was one of my favorite projects to help me get comfortable with a new programming language, or just give myself some good practice if I want to refresh myself on a language that I haven't worked with in awhile.

The challenge starts with the architecture specifications of a virtual machine that is needed to run a binary file. The one I have in this repository is the binary file that I had associated with my particular user, but the only difference is the codes generated to verify you solved the puzzle correctly.

Once you have implemented all the operations for the virtual machine that can execute the provided bin (it will run tests and let you know if anything is broken), you get to play a text-based adventure game which has even more puzzles!

The implementation I have done here only goes so far as to make the text-based adventure game work. There are some really cool puzzles to solve in order to make it to the end of the adventure game, though, so I plan to eventually come back to completing this solution in Java.

Design

My favorite thing about doing this project over the years is seeing how I can make the project even easier to read, debug, and try new visualizations.

For this attempt, I wanted to design it so that the virtual machine state was thread-safe and easy to 'undo'/'rollback' operations. Both of these make it so that I don't limit my options when it comes to implementing interfaces with additional functionality outside of the terminal.

Using the Builder Pattern is a clean way to achieve this, since the interface enforces that any operation on the virtual machine does not change existing state, it creates new states.

The implementation of operations is also decoupled from the virtual machine logic, which does mean a different set of operations could be implemented and ran on the virtual machine. This allows developers to easily implement custom operations that can do things like change state without changing the address. This decoupling also makes it much easier to write unit tests for both the virtual machine and the operations.

Here is what this looks like in code. To create a new virtual machine that reads from stdin, writes to stdout, and is loaded with a program at path:

VirtualMachine vm = new VirtualMachine.Builder(path)
    .setInputReader(new BufferedReader(new InputStreamReader(System.in)))
    .setOutputWriter(new BufferedWriter(new OutputStreamerWriter(System.out)))
    .build();

To take a single step through the program, it is extremely simple.

int opCode = vm.readMemory(vm.getAddress());
OpCode operation = OpCode.fromCode(opCode);
VirtualMachine newVmState = operation.takeStep(vm);

It is as simple as looking up what opCode is at the current memory address, looking up the corresponding operation based on that opCode, and using that operation to create the new virtual machine state.

If you wanted to keep a history that you could easily use to take steps back, you could use something like a stack or deque to place previous states. At that point, taking a step back just means popping off the VirtualMachine on top of the stack and operating off that.

VirtualMachine vm = new VirtualMachine.Builder(path)
    .setInputReader(new BufferedReader(new InputStreamReader(System.in)))
    .setOutputWriter(new BufferedWriter(new OutputStreamerWriter(System.out)))
    .build();
Deque<VirtualMachine> history = new ArrayDeque<>();

// Exhaust the VM until it can't run anymore
while (vm != null) {
  int opCode = vm.readMemory(vm.getAddress());
  OpCode operation = OpCode.fromCode(opCode);
  history.push(vm);
  vm = operation.takeStep(vm);
}

// Walk backwards through time and display the registers and local memory in
//   reverse order!
while (!history.isEmpty()) {
  VirtualMachine v = history.pop();
  System.out.println(v.toString());
}

Running the CLI

To run this Java-based Synacor VM in CLI mode (reads from stdin, writes to stdout):

./mvnw exec:java -Dexec.args="[arguments]"
  • Replace [arguments] with any CLI options your VM supports (e.g., -f input/challenge.bin).
  • Example:
    ./mvnw exec:java -Dexec.args="-f input/challenge.bin"
    

VM specification

You can find the specification for the VM's architecture and available methods in arch-spec.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages