Implement debug commands, like saving and loading state

This commit is contained in:
RunasSudo 2016-05-29 18:23:32 +09:30
parent 753a7a9ec6
commit 2eae52efac
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
5 changed files with 104 additions and 11 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/challenge.bin
/dumps

View File

@ -1,3 +1,23 @@
# synacor.py
My sort-of-OOP, ~~poorly-documented~~concise response to the Synacor challenge
## Debug commands
At any time the program is waiting for input, a string of the following form may be input:
.<cmd> <args>
This will execute the file `<cmd>.py` with `dbg_args[0]` set to `<cmd>` and `<args>` stored in `dbg_args[1..n]`.
For example, the self-test and decryption at the beginning of the program takes a comparatively long time. To save the state to the `dumps/init` file, enter:
.dbg_dump dumps/init
Similarly, debug commands may be passed as command-line arguments to `synacor.py` in the form:
./synacor.py <cmd> <args>
For example, to load the `dumps/init` state to skip the self-test and decryption, run:
./synacor.py dbg_load dumps/init

30
dbg_dump.py Normal file
View File

@ -0,0 +1,30 @@
# synacor.py - An implementation of the Synacor Challenge
# Copyright © 2016 RunasSudo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pickle
if len(dbg_args) < 2:
print('Usage: .{} <file>'.format(dbg_args[0]))
else:
model = {
'SYN_PTR': SYN_PTR,
'SYN_MEM': SYN_MEM,
'SYN_REG': SYN_REG,
'SYN_STK': SYN_STK
}
with open(dbg_args[1], 'wb') as f:
pickle.dump(model, f)

28
dbg_load.py Normal file
View File

@ -0,0 +1,28 @@
# synacor.py - An implementation of the Synacor Challenge
# Copyright © 2016 RunasSudo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pickle
if len(dbg_args) < 2:
print('Usage: {} {} <file>'.format(sys.argv[0], dbg_args[0]))
else:
with open(dbg_args[1], 'rb') as f:
model = pickle.load(f)
SYN_PTR = model['SYN_PTR']
SYN_MEM = model['SYN_MEM']
SYN_REG = model['SYN_REG']
SYN_STK = model['SYN_STK']

View File

@ -22,6 +22,7 @@ SYN_PTR = 0
SYN_MEM = [0] * 32768
SYN_REG = [0] * 8
SYN_STK = []
SYN_STDIN_BUF = []
class OpLiteral:
def __init__(self, value):
@ -52,16 +53,20 @@ def swallowOp():
SYN_PTR += 1
return op
# Read code into memory
with open('challenge.bin', 'rb') as data:
while True:
byteData = data.read(2)
if len(byteData) < 2:
break
SYN_MEM[SYN_PTR] = struct.unpack('<H', byteData)[0]
SYN_PTR += 1
SYN_PTR = 0
if len(sys.argv) > 1:
dbg_args = sys.argv[1:]
with open(dbg_args[0] + '.py', 'r') as f:
exec(f.read(), globals(), locals())
else:
# Read code into memory
with open('challenge.bin', 'rb') as data:
while True:
byteData = data.read(2)
if len(byteData) < 2:
break
SYN_MEM[SYN_PTR] = struct.unpack('<H', byteData)[0]
SYN_PTR += 1
SYN_PTR = 0
# Begin execution
while True:
@ -120,6 +125,15 @@ while True:
elif instruction == 19: #OUT
print(chr(swallowOp().get()), end='')
elif instruction == 20: #IN
swallowOp().set(ord(sys.stdin.read(1))) # the spec says a whole line will be read, so ¯\_(ツ)_/¯
while len(SYN_STDIN_BUF) == 0:
line = sys.stdin.readline()
if line.startswith('.'): # debug command
dbg_args = line.rstrip()[1:].split()
with open(dbg_args[0] + '.py', 'r') as f:
SYN_PTR -= 1; exec(f.read(), globals(), locals()); SYN_PTR += 1
else:
SYN_STDIN_BUF = list(line)
swallowOp().set(ord(SYN_STDIN_BUF.pop(0)))
else:
raise Exception('Unimplemented opcode {} at {}'.format(instruction, SYN_PTR))