Closed drewish closed 6 years ago
Interesting. I'll think about this.
I've also been thinking about trying to write a Zoom plugin for ScottKit games: see http://www.logicalshift.co.uk/mac/zoom.html (I know there is a plugin facility but I can't find any documentation for how to hook into it.) If I get that done, it will probably involve similar changes to the core.
With a little poking I was able to get it down to this to externalize the IO:
require 'scottkit/game'
require 'scottkit/game'
class MyGame < ScottKit::Game
def gets
options[:input_fiber].resume
end
def puts *args
options[:output_buffer].puts *args
end
def print *args
options[:output_buffer].print *args
end
end
file_name = 'game.sao'
output = StringIO.new
my_fiber = Fiber.new do
while true
puts output.string
output.reopen('')
Fiber.yield(gets)
end
end
game = MyGame.new(input_fiber: my_fiber, output_buffer: output)
game.load(IO.read(file_name))
game.decompile(StringIO.new)
game.play
Going to see if I can get it hooked up to the slack client next.
Looks encouraging! Keep me updated.
Turns out my easy fix won't work. The callbacks from slack are coming from a different thread so the fiber isn't able to pass the input across. I'm going to see if I can refactor more of the functionality of the play method into smaller methods and then override the play method and invert the control flow.
I don't know enough about Slack to have thought on the detail of this approach. But bear this in mind as you progress: my plan with the play
method is to introduce the concept of a "driver" object that play
invokes several methods of -- crucially, input
, output
and describe
. A simple driver will retain the present play
behaviour in a scrolling dialogue; and a curses-based one will provide a ScottFree-like UI where the present location description is always in its own window at the top of the screen. (That's why I need to separate describe
from other output.)
Will that scheme help by giving you what you need? Or do you need to rip play
apart more brutally than that?
I'm really just trying to pull all the stuff out in there into separate methods so it would be easier to try externalizing the logic. I'll push up a work in progress branch so you can see what I'm looking at.
One thing I was noticing is that it the tests are failing in master it seems like maybe some of them weren't updated for the lighting changes? Do you have a script for running them? Would you be interested in getting Travis setup to run them as part of the PR process? I'm happy to help with that if so.
The tests need a bit of looking at.
Some of them are rather too precise in what they expect -- for example, there's a complete transcript of solving Scott Adams's Adventureland, which is expected to remain the same. Whenever I change anything in the output format, those tests break and need to be tediously reconfigured. (Check through the git logs, and you'll see a lot of "Rebuild regression expectations" commits.)
I think what we need is a top-level rake target that rebuilds all of the test expectations automatically, so that once we are convinced the code is code we can make an output-format change, then run rake regenerate
, and know the tests will work.
I guess I should have said all that in issue #12 instead. I'll copy it there. Let's discuss on that issue rather than here.
I had a wild idea that I'd like to try to hook up the scottkit play code to a slack bot so you could play it via chat. I've played around with trying to treat it as a shell script and work at the stdin/stdout level but turned into a pretty big PITA. I went in and stripped the binary down to just:
I think I can just use an
StringIO
with$stdout
to capture the output, clearing it between turns... though I suspect I'll addputs
andprint
methods that target my buffer so I don't have to worry about anything else logging to the global variable.But I'm not sure quite how to handle the input. The
play
method's loop calls into thegets
method so that seems like a pretty obvious place to hook in. I'm wondering if it'd be feasible to fiber to pipe the input from the slack messages to the game.gets
would callthread.resume
and wait for input then when slack got something it could callyield
to send it across.Any thoughts?