Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instructions print via game #32

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions bin/scottkit
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ $VERBOSE = true
require 'optparse'
require 'stringio'
require 'scottkit/game'
require 'scottkit/withio'


def play(game)
Expand Down Expand Up @@ -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
Expand Down
86 changes: 41 additions & 45 deletions lib/scottkit/compile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#
Expand All @@ -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"
Expand Down Expand Up @@ -81,6 +79,8 @@ def parse
items, actions, verbgroups, noungroups)
end

private

def parse_room
match :room
name = match :symbol
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
46 changes: 33 additions & 13 deletions lib/scottkit/game.rb
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
# <tt>$stdout</tt> if nothing is provided.
#
# [+input+] If specified, a readable IO stream for
# capturing the game's input. Defaults to
# <tt>$stdin</tt> 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
Expand Down Expand Up @@ -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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about overriding print in game.rb. Isn't the more honest approach you used in the previous commit also suitable here?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ... but you're not overriding print, are you? You're just defining a method with the same name as the underlying primitive that it wraps -- so the invocation changes from print(x) to @game.print(x). OK, that makes perfect sense. Carry on :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah kind of funky to have to do this but it's either this or passing the output object down to the instructions. This seemed cleaner and having it centralized would give you one place to hook in a driver.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this is good.

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]
Expand Down Expand Up @@ -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
Expand All @@ -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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not in love with this argument ordering but it felt like the least bad option considering the optional fh argument. If it was me I'd just ditch this method and let the callers instantiate and call the compiler themselves.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it was me I'd just ditch this method and let the callers instantiate and call the compiler themselves.

I'm not following. What would that look like?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll respond on the main thread to keep things centrally visible.

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()

Expand Down
21 changes: 11 additions & 10 deletions lib/scottkit/play.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -457,17 +458,17 @@ 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
@game.counter, @game.counters[which] =
@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
Expand Down
15 changes: 0 additions & 15 deletions lib/scottkit/withio.rb

This file was deleted.

Loading