diff --git a/bin/scottkit b/bin/scottkit
index 789cd33..5da3529 100755
--- a/bin/scottkit
+++ b/bin/scottkit
@@ -17,7 +17,6 @@ $VERBOSE = true
require 'optparse'
require 'stringio'
require 'scottkit/game'
-require 'scottkit/withio'
def play(game)
@@ -110,19 +109,17 @@ exitval = 0
game = ScottKit::Game.new(options)
case mode
when :play_from_source
- f = StringIO.new
- withIO(nil, f) do
- if !game.compile_to_stdout(ARGV[0])
- $stderr.puts "#$0: compilation failed: not playing"
- exitval = 1
- end
+ compiled_game = StringIO.new
+ if !game.compile(compiled_game, ARGV[0])
+ $stderr.puts "#$0: compilation failed: not playing"
+ exitval = 1
end
if exitval == 0
- game.load(f.string)
+ game.load(compiled_game.string)
play(game)
end
when :compile
- if !game.compile_to_stdout(ARGV[0])
+ if !game.compile($stdout, ARGV[0])
$stderr.puts "#$0: compilation failed"
exitval = 1
end
diff --git a/lib/scottkit/compile.rb b/lib/scottkit/compile.rb
index a66c0ac..4fd2d4f 100644
--- a/lib/scottkit/compile.rb
+++ b/lib/scottkit/compile.rb
@@ -5,8 +5,6 @@
module ScottKit
class Game
class Compiler
- private
-
# Creates a new compiler for the specified game, set up ready to
# compile the game in the specified file.
#
@@ -25,17 +23,17 @@ def initialize(game, filename, fh = nil)
end
# Compiles the specified game-source file, writing the resulting
- # object file to stdout, whence it should be redirected into a
+ # object file to an IO stream, whence it should be redirected into a
# file so that it can be played. Yes, this API is sucky: it
# would be better if we had a simple compile method that builds
# the game in memory in a form that can by played, and which can
# then also be saved as an object file by some other method --
# but that would have been more work for little gain.
#
- def compile_to_stdout
+ def compile_to(output)
begin
tree = parse
- generate_code(tree)
+ generate_code(tree, output)
true
rescue
return false if String($!) == "syntax error"
@@ -81,6 +79,8 @@ def parse
items, actions, verbgroups, noungroups)
end
+ private
+
def parse_room
match :room
name = match :symbol
@@ -139,7 +139,7 @@ def parse_occur
end
conds, instructions, comment = parse_actionbody
- CAction.new(nil, chance, conds, instructions, comment)
+ CAction.new(nil, chance, conds, instructions, comment)
end
def parse_actionbody
@@ -207,7 +207,7 @@ def match(token, estr = nil); @lexer.match token, estr; end
def error(str); @lexer.error str; end
- def generate_code(tree)
+ def generate_code(tree, output)
lintOptions = @game.options[:lint] || ''
@had_errors = false
rooms = tree.rooms
@@ -267,7 +267,7 @@ def generate_code(tree)
dead_ends = []
rooms.each.with_index do |room, index|
next if index == 0
- next if
+ next if
if room.exits.length == 0
no_exits.push room.name
elsif room.exits.length == 1
@@ -439,76 +439,76 @@ def generate_code(tree)
return if @had_errors
# Write header
- puts tree.unknown1 || 0
- puts items.size-1
- puts actions.size-1
- puts verbs.size-1
- puts rooms.size-1
- puts tree.maxload || -1
- puts startindex
- puts items.select { |x| x.desc[0] == "*" }.count
- puts tree.wordlen
- puts tree.lighttime || -1
- puts messages.size-1
- puts treasuryindex
- puts # Blank line
+ output.puts tree.unknown1 || 0
+ output.puts items.size-1
+ output.puts actions.size-1
+ output.puts verbs.size-1
+ output.puts rooms.size-1
+ output.puts tree.maxload || -1
+ output.puts startindex
+ output.puts items.select { |x| x.desc[0] == "*" }.count
+ output.puts tree.wordlen
+ output.puts tree.lighttime || -1
+ output.puts messages.size-1
+ output.puts treasuryindex
+ output.puts # Blank line
# Actions
actions.each do |action|
- print 150*action.verb + action.noun, " "
+ output.print 150*action.verb + action.noun, " "
- print action.conds.map { |x| String(x[0] + 20 * x[1]) + " " }.join
- print action.gathered_args.map { |x| String(20*x) + " " }.join
+ output.print action.conds.map { |x| String(x[0] + 20 * x[1]) + " " }.join
+ output.print action.gathered_args.map { |x| String(20*x) + " " }.join
nconds = action.conds.size + action.gathered_args.size
raise "condition has #{nconds} conditions" if nconds > 5
- (5-nconds).times { print "0 " }
+ (5-nconds).times { output.print "0 " }
ins = action.instructions.map { |x| x[0] }
(4-ins.count).times { ins << 0 }
- puts "#{150*ins[0] + ins[1]} #{150*ins[2] + ins[3]}\n"
+ output.puts "#{150*ins[0] + ins[1]} #{150*ins[2] + ins[3]}\n"
end
- puts # Blank line
+ output.puts # Blank line
# Vocab
verbs.each.with_index do |verb, i|
- puts "\"#{verb}\" \"#{nouns[i]}\""
+ output.puts "\"#{verb}\" \"#{nouns[i]}\""
end
- puts # Blank line
+ output.puts # Blank line
# Rooms
rooms.each do |room|
0.upto(5).each do |i|
exit = room.exits[@game.dirname(i)]
- print(exit ? exit : 0, " ")
+ output.print(exit ? exit : 0, " ")
end
- print "\"#{room.desc}\"\n"
+ output.print "\"#{room.desc}\"\n"
end
- puts # Blank line
+ output.puts # Blank line
# Messages
messages.each do |message|
- puts "\"#{message}\"\n"
+ output.puts "\"#{message}\"\n"
end
- puts # Blank line
+ output.puts # Blank line
# Items
items.each do |item|
desc = item.desc
desc += "/" + item.called.upcase[0, @wordlen] + "/" if item.called
- puts "\"#{desc}\" #{item.where}"
+ output.puts "\"#{desc}\" #{item.where}"
end
- puts # Blank line
+ output.puts # Blank line
# Action comments
actions.each do |action|
- puts "\"#{action.comment || ""}\"\n"
+ output.puts "\"#{action.comment || ""}\"\n"
end
- puts # Blank line
+ output.puts # Blank line
# Trailer
- puts tree.version || 0
- puts tree.ident || 0
- puts tree.unknown2 || 0
+ output.puts tree.version || 0
+ output.puts tree.ident || 0
+ output.puts tree.unknown2 || 0
end
def room_by_name(loc, roommap)
@@ -560,10 +560,6 @@ def gwarning(str)
0
end
- public :compile_to_stdout # Must be visible to Game.compile()
- public :parse # Used by test_compile.rb
-
-
CGame = Struct.new(:ident, :version, :unknown1, :unknown2,
:start, :treasury, :maxload, :wordlen,
:lighttime, :lightsource, :rooms, :items,
diff --git a/lib/scottkit/game.rb b/lib/scottkit/game.rb
old mode 100755
new mode 100644
index 7130fdf..c5a0db1
--- a/lib/scottkit/game.rb
+++ b/lib/scottkit/game.rb
@@ -20,11 +20,12 @@ class Game
attr_reader :flags, :counters, :saved_rooms, :noun #:nodoc:
attr_accessor :loc, :counter, :saved_room, :lampleft #:nodoc:
- private
+ attr_reader :input, :output
# Creates a new game, with no room, items or actions -- load must
- # be called to make the game ready for playing, or
- # compile_to_stdout can be called to generate a new game-file.
+ # be called to make the game ready for playing, or compile can be
+ # called to generate a new game-file.
+ #
# The options hash affects various aspects of how the game will be
# loaded, played and compiled. The following symbols are
# recognised as keys in the options hash:
@@ -44,10 +45,18 @@ class Game
# file to restore before starting to play.
#
# [+read_file+] If specified, the name of a file of game
- # commands to be run after restoring s saved
+ # commands to be run after restoring a saved
# game (if any) and before starting to read
# commands from the user.
#
+ # [+output+] If specified, a writable IO stream for
+ # capturing the game's output. Defaults to
+ # $stdout if nothing is provided.
+ #
+ # [+input+] If specified, a readable IO stream for
+ # capturing the game's input. Defaults to
+ # $stdin if nothing is provided.
+ #
# [+echo_input+] If true, then game commands are echoed
# before being executed. This is useful
# primarily if input is being redirected
@@ -106,8 +115,25 @@ def initialize(options)
@options = options
@rooms, @items, @actions, @nouns, @verbs, @messages =
[], [], [], [], [], []
+ @input = options[:input] || $stdin
+ @output = options[:output] || $stdout
+ end
+
+ def puts *args
+ output.puts(*args)
+ end
+
+ def print *args
+ output.print(*args)
end
+ # Print debugging output
+ def dputs(level, *args) #:nodoc:
+ super.puts args.map { |x| "# #{x}" } if @options[level]
+ end
+
+ private
+
# Virtual accessor
def dark_flag #:nodoc:
@flags[FLAG_DARK]
@@ -261,10 +287,6 @@ def restore(name)
@items.each { |item| item.loc = f.gets.to_i }
end
- def dputs(level, *args) #:nodoc:
- puts args.map { |x| "# #{x}" } if @options[level]
- end
-
# Compiles the specified game-source file, writing the resulting
# object file to stdout, whence it should be redirected into a
# file so that it can be played. Yes, this API is sucky: it would
@@ -283,14 +305,12 @@ def dputs(level, *args) #:nodoc:
# function is that its behaviour is influenced by the game's
# options.)
#
- def compile_to_stdout(filename, fh = nil)
- compiler = ScottKit::Game::Compiler.new(self, filename, fh)
- compiler.compile_to_stdout
+ def compile(out, filename, fh = nil)
+ ScottKit::Game::Compiler.new(self, filename, fh).compile_to(out)
end
- public :load, :compile_to_stdout # Must be visible to driver program
+ public :load, :compile # Must be visible to driver program
public :roomname, :itemname # Needed by Condition.render()
- public :dputs # Needed for contained classes' debugging output
public :dirname # Needed by compiler
public :dark_flag= # Invoked from Instruction.execute()
diff --git a/lib/scottkit/play.rb b/lib/scottkit/play.rb
index c3d1421..5cf3abe 100644
--- a/lib/scottkit/play.rb
+++ b/lib/scottkit/play.rb
@@ -93,14 +93,15 @@ def process_lighting
end
end
- # Get a line from @fh if defined, otherwise $stdin
+ # Get a line from @fh if defined, otherwise input
def gets
line = nil
if (@fh)
line = @fh.gets
+ # Clear the read_file handle when all the input is consumed.
@fh = nil if !line
end
- line = $stdin.gets if !line
+ line = input.gets if !line
return nil if !line
puts line if @fh || options[:echo_input]
line
@@ -411,15 +412,15 @@ def execute(args)
if (@op == 0)
return false # shouldn't happen
elsif (@op <= 51)
- puts @game.messages[@op].gsub('`', '"')
+ @game.puts @game.messages[@op].gsub('`', '"')
return false
elsif (@op >= 102)
- puts @game.messages[@op-50].gsub('`', '"')
+ @game.puts @game.messages[@op-50].gsub('`', '"')
return false
else case @op
when 52 then
if @game.ncarried == @game.maxload
- puts "I've too much to carry!"
+ @game.puts "I've too much to carry!"
else
@game.items[args.shift].loc = ROOM_CARRIED
end
@@ -432,7 +433,7 @@ def execute(args)
when 59 then @game.items[args.shift].loc = ROOM_NOWHERE
when 60 then @game.flags[args.shift] = false
when 61 then
- puts "I am dead."; @game.flags[15] = false;
+ @game.puts "I am dead."; @game.flags[15] = false;
@game.loc = @game.rooms.size-1; @game.need_to_look
when 62 then i = args.shift; @game.items[i].loc = args.shift
when 63 then @game.finish(0)
@@ -457,7 +458,7 @@ def execute(args)
@game.items[i1].loc = @game.items[i2].loc
when 76 then @game.need_to_look
when 77 then @game.counter -= 1
- when 78 then print @game.counter, " "
+ when 78 then @game.print @game.counter, " "
when 79 then @game.counter = args.shift
when 80 then @game.loc, @game.saved_room = @game.saved_room, @game.loc
when 81 then which = args.shift
@@ -465,9 +466,9 @@ def execute(args)
@game.counters[which], @game.counter
when 82 then @game.counter += args.shift
when 83 then @game.counter -= args.shift
- when 84 then print @game.noun
- when 85 then puts @game.noun
- when 86 then puts
+ when 84 then @game.print @game.noun
+ when 85 then @game.puts @game.noun
+ when 86 then @game.puts
when 87 then which = args.shift
@game.loc, @game.saved_rooms[which] =
@game.saved_rooms[which], @game.loc
diff --git a/lib/scottkit/withio.rb b/lib/scottkit/withio.rb
deleted file mode 100644
index e35e388..0000000
--- a/lib/scottkit/withio.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# Provides a utility method withIO() used by several test-cases. Runs
-# the specified block with stdin and stdout replumbed to the provided
-# file-handles; the old values of stdin and stdout are passed to the
-# block, in case they should be needed.
-
-def withIO(newin, newout)
- old_STDIN = $stdin
- old_STDOUT = $stdout
- $stdin = newin
- $stdout = newout
- yield old_STDIN, old_STDOUT
-ensure
- $stdin = old_STDIN
- $stdout = old_STDOUT
-end
diff --git a/scottkit.gemspec b/scottkit.gemspec
index 803dd3f..eb23821 100644
--- a/scottkit.gemspec
+++ b/scottkit.gemspec
@@ -99,7 +99,6 @@ Gem::Specification.new do |s|
"lib/scottkit/decompile.rb",
"lib/scottkit/game.rb",
"lib/scottkit/play.rb",
- "lib/scottkit/withio.rb",
"scottkit.gemspec",
"test/test_canonicalise.rb",
"test/test_compile.rb",
@@ -107,7 +106,6 @@ Gem::Specification.new do |s|
"test/test_play.rb",
"test/test_playadams.rb",
"test/test_save.rb",
- "test/withio_test.rb"
]
s.homepage = "http://github.com/MikeTaylor/scottkit".freeze
s.licenses = ["GPL-2.0".freeze]
diff --git a/test/test_canonicalise.rb b/test/test_canonicalise.rb
index 79d5a26..c6c5438 100644
--- a/test/test_canonicalise.rb
+++ b/test/test_canonicalise.rb
@@ -1,7 +1,6 @@
require 'test/unit'
require 'scottkit/game'
require 'stringio'
-require 'scottkit/withio'
# The idea here is that we can start with a source file and compile it
# once. Decompiling the result will of course yield a different
@@ -17,7 +16,7 @@ def test_1canonicalise_tutorials
def test_2canonicalise_crystal
canonicalise("crystal/crystal.sck", :source)
end
-
+
def test_3canonicalise_adams
%w{
adv01
@@ -44,7 +43,7 @@ def test_3canonicalise_adams
x[0] = ""
options[:bug_tolerant] = true
end
- canonicalise("adams/#{x}.dat", :object, options)
+ canonicalise("adams/#{x}.dat", :object, options)
end
end
@@ -76,12 +75,10 @@ def canonicalise(name, type, options = {})
def compile(source, options)
game = ScottKit::Game.new(options)
- f = StringIO.new
- withIO(nil, f) do
- game.compile_to_stdout(nil, StringIO.new(source)) or
- raise "couldn't compile"
- end
- f.string
+ output = StringIO.new
+ game.compile(output, nil, StringIO.new(source)) or
+ raise "couldn't compile"
+ output.string
end
def decompile(object, options)
diff --git a/test/test_compile.rb b/test/test_compile.rb
index 5bb3d29..5560103 100644
--- a/test/test_compile.rb
+++ b/test/test_compile.rb
@@ -1,7 +1,6 @@
require 'test/unit'
require 'scottkit/game'
require 'stringio'
-require 'scottkit/withio'
class TestCompile < Test::Unit::TestCase #:nodoc:
EXPECTED = [
@@ -44,11 +43,9 @@ def test_parser
def test_code_generator
game = ScottKit::Game.new({})
- f = StringIO.new
- withIO(nil, f) do
- game.compile_to_stdout("games/test/crystal.sck") or
- raise "couldn't compile crystal.sck"
- end
- assert_equal(f.string, File.read("games/test/crystal.sao"))
+ compiled_game = StringIO.new
+ game.compile(compiled_game, "games/test/crystal.sck") or
+ raise "couldn't compile crystal.sck"
+ assert_equal(compiled_game.string, File.read("games/test/crystal.sao"))
end
end
diff --git a/test/test_play.rb b/test/test_play.rb
index 4602b33..0ca5f95 100644
--- a/test/test_play.rb
+++ b/test/test_play.rb
@@ -1,20 +1,19 @@
require 'test/unit'
require 'scottkit/game'
require 'stringio'
-require 'scottkit/withio'
class TestPlay < Test::Unit::TestCase #:nodoc:
def test_play_tutorial7; play("t7"); end
def test_play_crystal; play("crystal"); end
def play(name)
- game = ScottKit::Game.new({ :read_file =>
- "games/test/#{name}.solution",
- :random_seed => 12368, :no_wait => true })
+ game = ScottKit::Game.new(read_file: "games/test/#{name}.solution",
+ output: StringIO.new, random_seed: 12368,
+ no_wait: true)
game.load(IO.read("games/test/#{name}.sao"))
- f = StringIO.new
- withIO(nil, f) { game.play }
+ game.play
+
expected = File.read("games/test/#{name}.transcript")
- assert_equal(expected, f.string)
+ assert_equal(expected, game.output.string)
end
end
diff --git a/test/test_playadams.rb b/test/test_playadams.rb
index de091c4..4efbcd5 100644
--- a/test/test_playadams.rb
+++ b/test/test_playadams.rb
@@ -2,7 +2,6 @@
require 'scottkit/game'
require 'stringio'
require 'digest/md5'
-require 'scottkit/withio'
class TestPlayAdams < Test::Unit::TestCase #:nodoc:
def test_play_adams
@@ -15,13 +14,12 @@ def play(name)
gamefile = "games/adams/#{name}.dat"
if(File::readable?(gamefile))
- game = ScottKit::Game.new({ :read_file =>
- "games/test/adams/#{name}.solution",
- :random_seed => 12368, :no_wait => true })
+ game = ScottKit::Game.new(output: StringIO.new,
+ read_file: "games/test/adams/#{name}.solution",
+ random_seed: 12368, no_wait: true )
game.load(IO.read gamefile)
- f = StringIO.new
- withIO(nil, f) { game.play }
- digest = Digest::MD5.hexdigest(f.string)
+ game.play
+ digest = Digest::MD5.hexdigest(game.output.string)
expected = File.read("games/test/adams/#{name}.transcript.md5").chomp
assert_equal(expected, digest)
else
diff --git a/test/test_save.rb b/test/test_save.rb
index ff7693e..a6e174e 100644
--- a/test/test_save.rb
+++ b/test/test_save.rb
@@ -1,30 +1,28 @@
require 'test/unit'
require 'scottkit/game'
require 'stringio'
-require 'scottkit/withio'
class TestSave < Test::Unit::TestCase #:nodoc:
# Can't use a setup() method here as the two test-cases need the
# games to be initialised with different options.
def test_save_crystal
- game = ScottKit::Game.new({ :random_seed => 12368, :echo_input => true })
+ game = ScottKit::Game.new(random_seed: 12368, echo_input: true,
+ input: File.new("games/test/crystal.save-script"),
+ output: StringIO.new)
game.load(IO.read("games/test/crystal.sao"))
- withIO(File.new("games/test/crystal.save-script"),
- File.new("/dev/null", "w")) do
- game.play()
- end
+ game.play()
assert_equal(File.read("TMP"), File.read("games/test/crystal.save-file"))
File.unlink "TMP"
end
def test_resave_crystal
- game = ScottKit::Game.new({ :random_seed => 12368, :echo_input => true,
- :restore_file => "games/test/crystal.save-file" })
+ game = ScottKit::Game.new(random_seed: 12368, echo_input: true,
+ input: StringIO.new("save game\nTMP"),
+ output: StringIO.new,
+ restore_file: "games/test/crystal.save-file")
game.load(IO.read("games/test/crystal.sao"))
- withIO(StringIO.new("save game\nTMP"), File.new("/dev/null", "w")) do
- game.play()
- end
+ game.play()
assert_equal(File.read("TMP"), File.read("games/test/crystal.save-file"))
File.unlink "TMP"
end
diff --git a/test/withio_test.rb b/test/withio_test.rb
deleted file mode 100755
index 3f2b6c9..0000000
--- a/test/withio_test.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# Run as: echo Bart Lisa | ./withio_test.rb && echo == && cat /tmp/xcv
-# Expecting the output:
-# first line is 'Bart Lisa'
-# saved line 'Eric'
-# died in withIO: divided by 0
-# saved line was 'Eric'
-# ==
-# first line was 'Bart Lisa'
-# oldout is of class IO
-
-require 'stringio'
-require 'scottkit/withio'
-
-saved = nil
-begin
- line = gets
- puts "first line is '#{line.chomp}'"
- withIO(StringIO.new("Eric\nthe"), File.new("/tmp/xcv", "w")) do
- |oldin, oldout|
- puts "first line was '#{line.chomp}'"
- puts "oldout is of class #{oldout.class}"
- saved = gets
- oldout.puts "saved line '#{saved.chomp}'"
- puts 1/0
- end
-rescue Exception => e
- puts "died in withIO: #{e}"
-end
-puts "saved line was '#{saved.chomp}'"