scottkit 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/GPL-2 +339 -0
- data/Makefile +5 -0
- data/README +75 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/bin/scottkit +96 -0
- data/bin/scottkit.rb +96 -0
- data/data/.gitignore +1 -0
- data/data/adams/.gitignore +2 -0
- data/data/adams/AdamsGames.zip +0 -0
- data/data/adams/Makefile +18 -0
- data/data/crystal/crystal.map +112 -0
- data/data/crystal/crystal.sck +598 -0
- data/data/crystal/crystal.solution +82 -0
- data/data/dan-and-matt.sck +180 -0
- data/data/dan-and-matt.solution +32 -0
- data/data/howarth/.gitignore +2 -0
- data/data/howarth/Makefile +14 -0
- data/data/howarth/mysterious.tar.gz +0 -0
- data/data/test/Makefile +18 -0
- data/data/test/adams/Makefile +13 -0
- data/data/test/adams/adv01.solution +186 -0
- data/data/test/adams/adv01.transcript +869 -0
- data/data/test/adams/adv01.transcript.md5 +1 -0
- data/data/test/adams/adv02.solution +225 -0
- data/data/test/adams/adv02.transcript +970 -0
- data/data/test/adams/adv02.transcript.md5 +1 -0
- data/data/test/adams/adv04.solution +187 -0
- data/data/test/adams/adv04.transcript +876 -0
- data/data/test/adams/adv04.transcript.md5 +1 -0
- data/data/test/crystal.decompile +628 -0
- data/data/test/crystal.sao +373 -0
- data/data/test/crystal.save-file +62 -0
- data/data/test/crystal.save-script +41 -0
- data/data/test/crystal.sck +598 -0
- data/data/test/crystal.solution +82 -0
- data/data/test/crystal.transcript +413 -0
- data/data/test/t6.pretty-print +225 -0
- data/data/test/t7.sao +110 -0
- data/data/test/t7.solution +28 -0
- data/data/test/t7.transcript +147 -0
- data/data/tutorial/t1.sck +5 -0
- data/data/tutorial/t2.sck +14 -0
- data/data/tutorial/t3.sck +38 -0
- data/data/tutorial/t4.sck +62 -0
- data/data/tutorial/t5.sck +87 -0
- data/data/tutorial/t6.sck +119 -0
- data/data/tutorial/t7.sck +135 -0
- data/lib/scottkit/compile.rb +661 -0
- data/lib/scottkit/decompile.rb +175 -0
- data/lib/scottkit/game.rb +409 -0
- data/lib/scottkit/play.rb +474 -0
- data/notes/Definition +147 -0
- data/notes/Definition.saved-game +9 -0
- data/notes/Definition.scottfree-1.14 +142 -0
- data/notes/adventureland-maze +20 -0
- data/notes/continue-action +51 -0
- data/test/test_canonicalise.rb +94 -0
- data/test/test_compile.rb +54 -0
- data/test/test_decompile.rb +13 -0
- data/test/test_play.rb +20 -0
- data/test/test_playadams.rb +36 -0
- data/test/test_save.rb +31 -0
- data/test/withio.rb +15 -0
- data/test/withio_test.rb +31 -0
- metadata +118 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
Game
|
2
|
+
|
3
|
+
A game consists of a header of 14 values each apparently 16 bit
|
4
|
+
|
5
|
+
0 Unknown
|
6
|
+
1 Number of items
|
7
|
+
2 Number of actions
|
8
|
+
3 Number of Nouns and Verbs (one list is padded)
|
9
|
+
4 Number of rooms
|
10
|
+
5 Maximum a player can carry
|
11
|
+
6 Starting Room
|
12
|
+
7 Total Treasures (*)
|
13
|
+
8 Word Length (only seen 3,4 or 5)
|
14
|
+
9 Time light source lasts. This counts down every time item 9 is
|
15
|
+
in game. Brian Howarths games allow -1 for never run down. When
|
16
|
+
it runs out the light item (9) is dumped in room 0 and a look
|
17
|
+
done. Messages vary between interpreters and include things
|
18
|
+
like 'Your light is flickering and dying' as well as
|
19
|
+
'Light runs out in %d turns'.
|
20
|
+
10 Number of Messages
|
21
|
+
11 Room you must put treasure in to score points. Not all games use
|
22
|
+
the treasure system for scoring
|
23
|
+
12 Unknown
|
24
|
+
|
25
|
+
All the number of something values are the last item to be read counting
|
26
|
+
from 0. Thus 3 messages, means messages 0, 1, 2 and 3.
|
27
|
+
|
28
|
+
(*) This can be calculated in game. What happens if the number is wrong
|
29
|
+
I don't know. I've found no games that it occurs in.
|
30
|
+
|
31
|
+
A game has 16 (maybe more) binary flags, and 8 (maybe more counters). A few
|
32
|
+
later games seem to have 2 (maybe more) values to store location numbers in
|
33
|
+
temporarily - eg Yoho spell in Claymorgue.
|
34
|
+
|
35
|
+
Following the header is a list of game actions. Each is of the form
|
36
|
+
|
37
|
+
150*verb+noun
|
38
|
+
5 repeats of condition+20*value
|
39
|
+
150*action1+action2
|
40
|
+
150*action3+action4
|
41
|
+
|
42
|
+
Conditions
|
43
|
+
|
44
|
+
0 Parameter
|
45
|
+
1 Item <arg> carried
|
46
|
+
2 Item <arg> in room with player
|
47
|
+
3 Item <arg> carried or in room with player
|
48
|
+
4 In room <arg>
|
49
|
+
5 Item <arg> not in room with player
|
50
|
+
6 Item <arg> not carried
|
51
|
+
7 Not in room <arg>
|
52
|
+
8 BitFlag <arg> is set.
|
53
|
+
9 BitFlag <arg> is cleared
|
54
|
+
10 Something carried (arg unused)
|
55
|
+
11 Nothing carried (arg unused)
|
56
|
+
12 Item <arg> not carried nor in room with player
|
57
|
+
13 Item <arg> is in game [not in room 0]
|
58
|
+
14 Item <arg> is not in game [in room 0]
|
59
|
+
15 CurrentCounter <= <arg>
|
60
|
+
16 CurrentCounter >= <arg>
|
61
|
+
17 Object still in initial room
|
62
|
+
18 Object not in initial room
|
63
|
+
19 CurrentCounter = <arg>
|
64
|
+
|
65
|
+
Actions
|
66
|
+
|
67
|
+
0 Does nothing
|
68
|
+
1-51 Print message 0-50. Some drivers add a space some add a newline.
|
69
|
+
52 Get item <arg>. Checks if you can carry it first
|
70
|
+
53 Drops item <arg>
|
71
|
+
54 Moves to room <arg>
|
72
|
+
55 Item <arg> is removed from the game (put in room 0)
|
73
|
+
56 The darkness flag is set
|
74
|
+
57 The darkness flag is cleared
|
75
|
+
58 Bitflag <arg> is set
|
76
|
+
59 The same as 55 (it seems - I'm cautious about this)
|
77
|
+
60 BitFlag <arg> is cleared
|
78
|
+
61 Death. Dark flag cleared, player moved to last room
|
79
|
+
62 Item <arg> put in room <arg>
|
80
|
+
63 Game over.
|
81
|
+
64 Describe room
|
82
|
+
65 Score
|
83
|
+
66 Inventory
|
84
|
+
67 BitFlag 0 is set
|
85
|
+
68 BitFlag 0 is cleared
|
86
|
+
69 Refill lamp (?)
|
87
|
+
70 Screen is cleared. This varies by driver from no effect upwards
|
88
|
+
71 Saves the game. Choices of filename etc depend on the driver alone.
|
89
|
+
72 Swap item <arg1> and item <arg2> locations
|
90
|
+
73 Continue with next line (the next line starts verb 0 noun 0)
|
91
|
+
74 Take item <arg> - no check is done too see if it can be carried.
|
92
|
+
75 Put item <arg1> with item <arg2> - Not certain seems to do this
|
93
|
+
from examination of Claymorgue
|
94
|
+
76 Look (same as 64 ?? - check)
|
95
|
+
77 Decrement current counter. Will not go below 0
|
96
|
+
78 Print current counter value. Some drivers only cope with 0-99
|
97
|
+
apparently
|
98
|
+
79 Set current counter value
|
99
|
+
80 Swap location with current location-swap flag
|
100
|
+
81 Select a counter. Current counter is swapped with backup counter
|
101
|
+
<arg>
|
102
|
+
82 Add <arg> to current counter
|
103
|
+
83 Subtract <arg> from current counter
|
104
|
+
84 Echo noun player typed without CR
|
105
|
+
85 Echo the noun the player typed
|
106
|
+
86 CR
|
107
|
+
87 Swap current location value with backup location-swap value <arg>
|
108
|
+
88 Wait 2 seconds
|
109
|
+
89 SAGA - draw picture <n> (actually <n+number of rooms>, as each
|
110
|
+
Look() draws picture <room number> automatically)
|
111
|
+
Older spectrum driver - crashes
|
112
|
+
Spectrum Seas of Blood - seems to start Fighting Fantasy combat mode
|
113
|
+
90-101 Unused
|
114
|
+
102+ Print message 51-99
|
115
|
+
|
116
|
+
|
117
|
+
This is followed by the words with verbs and nouns interleaved. A word with
|
118
|
+
a * at the beginning is a synonym for the word above.
|
119
|
+
Verb 1 is GO, verb 10 is GET, verb 18 is DROP (always).
|
120
|
+
Nouns 1-6 are directions.
|
121
|
+
|
122
|
+
This is followed by the rooms. Each room is 6 exits (north south east west
|
123
|
+
up down) followed by a text string.
|
124
|
+
|
125
|
+
Then come the messages, stored as a list of strings.
|
126
|
+
|
127
|
+
Next come the items in the format item text then location. Item text may
|
128
|
+
end with /TEXT/. This text is not printed but means that an automatic
|
129
|
+
get/drop will be done for 'GET/DROP TEXT' on this item. Item names beginning
|
130
|
+
with '*' are treasures. The '*' is printed. If you put all treasures in the
|
131
|
+
treasure room (in the header) and 'SCORE' the game finishes with a well done
|
132
|
+
message. Item location -1 is the inventory (255 on C64 and Spectrum tape
|
133
|
+
games) and 0 means not in play in every game I've looked at. The lamp (always
|
134
|
+
object 9) behaviour supports this belief.
|
135
|
+
|
136
|
+
A set of strings follow this. In order they match to each line of verb/noun
|
137
|
+
actions, and are comments. The Spectrum and C64 tape system where the
|
138
|
+
database is compiled into the program has no comments.
|
139
|
+
|
140
|
+
Finally three values follow which are version, adventure number and an
|
141
|
+
unknown magic number.
|
142
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Location Marker Exits
|
2
|
+
N S E W U D
|
3
|
+
-------------------------------------------------------------------------------
|
4
|
+
Cavern N/A N/A N/A N/A N/A SIGN
|
5
|
+
SIGN sign WU NEWU NEWU -- -- WU
|
6
|
+
NEWU SIGN -- WU SIGN WU --
|
7
|
+
WU -- -- -- SCRATCH SIGN --
|
8
|
+
SCRATCH scratchings ARROW SIGN WU SUD SUD ARROW
|
9
|
+
SUD -- ARROW -- -- SCRATCH ARROW
|
10
|
+
ARROW arrow, *rug* ARROW SIGN SIGN NEWU SCRATCH LAVA
|
11
|
+
LAVA lava, *net* -- -- -- -- ARROW --
|
12
|
+
|
13
|
+
From cavern to lava:
|
14
|
+
d
|
15
|
+
d
|
16
|
+
w
|
17
|
+
d
|
18
|
+
get rug
|
19
|
+
d
|
20
|
+
get net
|
@@ -0,0 +1,51 @@
|
|
1
|
+
How the "continue" action works. By inspection of Adventureland,
|
2
|
+
there is only one instance of it there:
|
3
|
+
|
4
|
+
action DRO WAT when carried bottle and at room1 {
|
5
|
+
print "Sizzle..."
|
6
|
+
continue
|
7
|
+
swap bottle bottle1
|
8
|
+
}
|
9
|
+
|
10
|
+
occur when here FIRESTONE {
|
11
|
+
swap now FIRESTONE
|
12
|
+
}
|
13
|
+
|
14
|
+
This is illuminating. First, continue doesn't have to be the last
|
15
|
+
instruction in an action: so it doesn't mean "proceed with the next
|
16
|
+
action", it means "note that when you're done with this action that
|
17
|
+
you may continue to the next". Second, note that the second action
|
18
|
+
has a condition which must be met, so the continue also doesn't mean
|
19
|
+
"actually do the next action", but "evaluate whether the next action
|
20
|
+
should be done". (That's why the continue is used here: the second
|
21
|
+
set of instructions should be executed only if the second condition is
|
22
|
+
true as well as the first.) The probability associated with follow-on
|
23
|
+
actions is ignored, though (and is probably always zero, to prevent
|
24
|
+
those actions firing in other circumstances).
|
25
|
+
|
26
|
+
Because conditions are still checked for actions reached by continue,
|
27
|
+
we can not fold actions linked by continue into a single long action
|
28
|
+
with multiple instructions, as tempting as that sounds.
|
29
|
+
|
30
|
+
--
|
31
|
+
|
32
|
+
More: it's apparent from Voodoo Castle that a continue action means
|
33
|
+
not just to attempt the next action, but ALL subsequent actions that
|
34
|
+
have verb and noun both equal to zero. Here is the decompilation of
|
35
|
+
the code for examining the Count:
|
36
|
+
|
37
|
+
action LOO COF when here Coffin1
|
38
|
+
print "A sign here says:"
|
39
|
+
print "Count Cristo's been CURSED! [...]"
|
40
|
+
continue
|
41
|
+
print "There's a man here"
|
42
|
+
|
43
|
+
occur 0% when !exists foot
|
44
|
+
print "He's wearing a rabbit's foot"
|
45
|
+
|
46
|
+
occur 0% when !exists ring
|
47
|
+
print "Wearing a sapphire ring"
|
48
|
+
|
49
|
+
At the start of the game, the rabbit's foot exists (elsewhere) and the
|
50
|
+
ring does not, so the first occur does not fire but the second does.
|
51
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'scottkit/game'
|
3
|
+
require 'stringio'
|
4
|
+
require_relative 'withio'
|
5
|
+
|
6
|
+
# The idea here is that we can start with a source file and compile it
|
7
|
+
# once. Decompiling the result will of course yield a different
|
8
|
+
# source file (for example, comments will be discarded) but
|
9
|
+
# recompiling the decompiled source should yield identical output to
|
10
|
+
# that generated by the initial compilation.
|
11
|
+
|
12
|
+
class TestCanonicalise < Test::Unit::TestCase #:nodoc:
|
13
|
+
def test_1canonicalise_tutorials
|
14
|
+
1.upto(7) { |i| canonicalise("tutorial/t#{i}.sck", :source) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_2canonicalise_crystal
|
18
|
+
canonicalise("crystal/crystal.sck", :source)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_3canonicalise_adams
|
22
|
+
%w{
|
23
|
+
adv01
|
24
|
+
adv02
|
25
|
+
adv03
|
26
|
+
adv04
|
27
|
+
adv05
|
28
|
+
adv06
|
29
|
+
adv07
|
30
|
+
adv08
|
31
|
+
adv09
|
32
|
+
adv10
|
33
|
+
adv11
|
34
|
+
adv12
|
35
|
+
adv13
|
36
|
+
adv14a
|
37
|
+
*adv14b
|
38
|
+
quest1
|
39
|
+
quest2
|
40
|
+
sampler1
|
41
|
+
}.each do |x|
|
42
|
+
options = {}
|
43
|
+
if x[0] == "*"
|
44
|
+
x[0] = ""
|
45
|
+
options[:bug_tolerant] = true
|
46
|
+
end
|
47
|
+
canonicalise("adams/#{x}.dat", :object, options)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def canonicalise(name, type, options = {})
|
52
|
+
#puts "#{name}: options=#{options}"
|
53
|
+
filename = "data/#{name}"
|
54
|
+
|
55
|
+
if !File::readable?(filename)
|
56
|
+
if self.respond_to? :skip
|
57
|
+
skip "no game file #{filename}"
|
58
|
+
else
|
59
|
+
# Crappy older version of Test::Unit
|
60
|
+
puts "skipping '#{name}' because no filename"
|
61
|
+
end
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
if type == :source
|
66
|
+
source1 = IO.read(filename)
|
67
|
+
else
|
68
|
+
object0 = IO.read(filename)
|
69
|
+
source1 = decompile(object0, options)
|
70
|
+
end
|
71
|
+
object1 = compile(source1, options)
|
72
|
+
source2 = decompile(object1, options)
|
73
|
+
object2 = compile(source2, options)
|
74
|
+
assert_equal(object2, object1)
|
75
|
+
end
|
76
|
+
|
77
|
+
def compile(source, options)
|
78
|
+
game = ScottKit::Game.new(options)
|
79
|
+
f = StringIO.new
|
80
|
+
withIO(nil, f) do
|
81
|
+
game.compile_to_stdout(nil, StringIO.new(source)) or
|
82
|
+
raise "couldn't compile"
|
83
|
+
end
|
84
|
+
f.string
|
85
|
+
end
|
86
|
+
|
87
|
+
def decompile(object, options)
|
88
|
+
game = ScottKit::Game.new(options)
|
89
|
+
game.load(object)
|
90
|
+
f = StringIO.new()
|
91
|
+
game.decompile(f)
|
92
|
+
f.string
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'scottkit/game'
|
3
|
+
require 'stringio'
|
4
|
+
require_relative 'withio'
|
5
|
+
|
6
|
+
class TestCompile < Test::Unit::TestCase #:nodoc:
|
7
|
+
EXPECTED = [
|
8
|
+
# Token, Lexeme
|
9
|
+
[ :room ],
|
10
|
+
[ :symbol, "chamber" ],
|
11
|
+
[ :symbol, "square chamber" ],
|
12
|
+
[ :exit ],
|
13
|
+
[ :direction, "east" ],
|
14
|
+
[ :symbol, "dungeon" ],
|
15
|
+
[ :room ],
|
16
|
+
[ :symbol, "dungeon" ],
|
17
|
+
[ :symbol, "gloomy dungeon" ],
|
18
|
+
[ :exit ],
|
19
|
+
[ :direction, "west" ],
|
20
|
+
[ :symbol, "chamber" ],
|
21
|
+
[ :eof ],
|
22
|
+
];
|
23
|
+
|
24
|
+
def test_lexer
|
25
|
+
game = ScottKit::Game.new({})
|
26
|
+
lexer = ScottKit::Game::Compiler::Lexer.new(game, "data/tutorial/t1.sck")
|
27
|
+
EXPECTED.each do |x| token, lexeme = *x
|
28
|
+
got = lexer.lex
|
29
|
+
assert_equal(token, got)
|
30
|
+
assert_equal(lexeme, lexer.lexeme) if lexeme
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_parser
|
35
|
+
game = ScottKit::Game.new({})
|
36
|
+
# It's a clumsy API, but then we're peeking where we're not invited
|
37
|
+
compiler = ScottKit::Game::Compiler.new(game, "data/tutorial/t6.sck")
|
38
|
+
tree = compiler.parse
|
39
|
+
got = tree.pretty_inspect
|
40
|
+
expected = File.read("data/test/t6.pretty-print")
|
41
|
+
# Remove hex object addresses from pretty-printed trees
|
42
|
+
assert_equal(got.gsub(/0x\h+/, ""), expected.gsub(/0x\h+/, ""))
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_code_generator
|
46
|
+
game = ScottKit::Game.new({})
|
47
|
+
f = StringIO.new
|
48
|
+
withIO(nil, f) do
|
49
|
+
game.compile_to_stdout("data/test/crystal.sck") or
|
50
|
+
raise "couldn't compile crystal.sck"
|
51
|
+
end
|
52
|
+
assert_equal(f.string, File.read("data/test/crystal.sao"))
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'scottkit/game'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
class TestDecompile < Test::Unit::TestCase #:nodoc:
|
6
|
+
def test_decompile_crystal
|
7
|
+
game = ScottKit::Game.new({})
|
8
|
+
game.load(IO.read("data/test/crystal.sao"))
|
9
|
+
f = StringIO.new()
|
10
|
+
game.decompile(f)
|
11
|
+
assert_equal(f.string, File.read("data/test/crystal.decompile"))
|
12
|
+
end
|
13
|
+
end
|
data/test/test_play.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'scottkit/game'
|
3
|
+
require 'stringio'
|
4
|
+
require_relative 'withio'
|
5
|
+
|
6
|
+
class TestPlay < Test::Unit::TestCase #:nodoc:
|
7
|
+
def test_play_tutorial7; play("t7"); end
|
8
|
+
def test_play_crystal; play("crystal"); end
|
9
|
+
|
10
|
+
def play(name)
|
11
|
+
game = ScottKit::Game.new({ :read_file =>
|
12
|
+
"data/test/#{name}.solution",
|
13
|
+
:random_seed => 12368, :no_wait => true })
|
14
|
+
game.load(IO.read("data/test/#{name}.sao"))
|
15
|
+
f = StringIO.new
|
16
|
+
withIO(nil, f) { game.play }
|
17
|
+
expected = File.read("data/test/#{name}.transcript")
|
18
|
+
assert_equal(expected, f.string)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'scottkit/game'
|
3
|
+
require 'stringio'
|
4
|
+
require 'digest/md5'
|
5
|
+
require_relative 'withio'
|
6
|
+
|
7
|
+
class TestPlayAdams < Test::Unit::TestCase #:nodoc:
|
8
|
+
def test_play_adams
|
9
|
+
%w{01 02 04}.each do |num|
|
10
|
+
play("adv#{num}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def play(name)
|
15
|
+
gamefile = "data/adams/#{name}.dat"
|
16
|
+
|
17
|
+
if(File::readable?(gamefile))
|
18
|
+
game = ScottKit::Game.new({ :read_file =>
|
19
|
+
"data/test/adams/#{name}.solution",
|
20
|
+
:random_seed => 12368, :no_wait => true })
|
21
|
+
game.load(IO.read gamefile)
|
22
|
+
f = StringIO.new
|
23
|
+
withIO(nil, f) { game.play }
|
24
|
+
digest = Digest::MD5.hexdigest(f.string)
|
25
|
+
expected = File.read("data/test/adams/#{name}.transcript.md5").chomp
|
26
|
+
assert_equal(expected, digest)
|
27
|
+
else
|
28
|
+
if self.respond_to? :skip
|
29
|
+
skip "no game file #{gamefile}"
|
30
|
+
else
|
31
|
+
# Crappy older version of Test::Unit
|
32
|
+
puts "skipping '#{name}' because no gamefile"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/test/test_save.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'scottkit/game'
|
3
|
+
require 'stringio'
|
4
|
+
require_relative 'withio'
|
5
|
+
|
6
|
+
class TestSave < Test::Unit::TestCase #:nodoc:
|
7
|
+
# Can't use a setup() method here as the two test-cases need the
|
8
|
+
# games to be initialised with different options.
|
9
|
+
|
10
|
+
def test_save_crystal
|
11
|
+
game = ScottKit::Game.new({ :random_seed => 12368, :echo_input => true })
|
12
|
+
game.load(IO.read("data/test/crystal.sao"))
|
13
|
+
withIO(File.new("data/test/crystal.save-script"),
|
14
|
+
File.new("/dev/null", "w")) do
|
15
|
+
game.play()
|
16
|
+
end
|
17
|
+
assert_equal(File.read("TMP"), File.read("data/test/crystal.save-file"))
|
18
|
+
File.unlink "TMP"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_resave_crystal
|
22
|
+
game = ScottKit::Game.new({ :random_seed => 12368, :echo_input => true,
|
23
|
+
:restore_file => "data/test/crystal.save-file" })
|
24
|
+
game.load(IO.read("data/test/crystal.sao"))
|
25
|
+
withIO(StringIO.new("save game\nTMP"), File.new("/dev/null", "w")) do
|
26
|
+
game.play()
|
27
|
+
end
|
28
|
+
assert_equal(File.read("TMP"), File.read("data/test/crystal.save-file"))
|
29
|
+
File.unlink "TMP"
|
30
|
+
end
|
31
|
+
end
|
data/test/withio.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Provides a utility method withIO() used by several test-cases. Runs
|
2
|
+
# the specified block with stdin and stdout replumbed to the provided
|
3
|
+
# file-handles; the old values of stdin and stdout are passed to the
|
4
|
+
# block, in case they should be needed.
|
5
|
+
|
6
|
+
def withIO(newin, newout)
|
7
|
+
old_STDIN = $stdin
|
8
|
+
old_STDOUT = $stdout
|
9
|
+
$stdin = newin
|
10
|
+
$stdout = newout
|
11
|
+
yield old_STDIN, old_STDOUT
|
12
|
+
ensure
|
13
|
+
$stdin = old_STDIN
|
14
|
+
$stdout = old_STDOUT
|
15
|
+
end
|
data/test/withio_test.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/local/bin/ruby1.9 -w
|
2
|
+
|
3
|
+
# Run as: echo Bart Lisa | ./withio_test.rb && echo == && cat /tmp/xcv
|
4
|
+
# Expecting the output:
|
5
|
+
# first line is 'Bart Lisa'
|
6
|
+
# saved line 'Eric'
|
7
|
+
# died in withIO: divided by 0
|
8
|
+
# saved line was 'Eric'
|
9
|
+
# ==
|
10
|
+
# first line was 'Bart Lisa'
|
11
|
+
# oldout is of class IO
|
12
|
+
|
13
|
+
require 'stringio'
|
14
|
+
require_relative 'withio'
|
15
|
+
|
16
|
+
saved = nil
|
17
|
+
begin
|
18
|
+
line = gets
|
19
|
+
puts "first line is '#{line.chomp}'"
|
20
|
+
withIO(StringIO.new("Eric\nthe"), File.new("/tmp/xcv", "w")) do
|
21
|
+
|oldin, oldout|
|
22
|
+
puts "first line was '#{line.chomp}'"
|
23
|
+
puts "oldout is of class #{oldout.class}"
|
24
|
+
saved = gets
|
25
|
+
oldout.puts "saved line '#{saved.chomp}'"
|
26
|
+
puts 1/0
|
27
|
+
end
|
28
|
+
rescue Exception => e
|
29
|
+
puts "died in withIO: #{e}"
|
30
|
+
end
|
31
|
+
puts "saved line was '#{saved.chomp}'"
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: scottkit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Taylor
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-28 00:00:00 +00:00
|
13
|
+
default_executable: scottkit
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: "TODO: longer description of your gem"
|
17
|
+
email: mike@miketaylor.org.uk
|
18
|
+
executables:
|
19
|
+
- scottkit
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- GPL-2
|
27
|
+
- Makefile
|
28
|
+
- README
|
29
|
+
- Rakefile
|
30
|
+
- VERSION
|
31
|
+
- bin/scottkit
|
32
|
+
- bin/scottkit.rb
|
33
|
+
- data/.gitignore
|
34
|
+
- data/adams/.gitignore
|
35
|
+
- data/adams/AdamsGames.zip
|
36
|
+
- data/adams/Makefile
|
37
|
+
- data/crystal/crystal.map
|
38
|
+
- data/crystal/crystal.sck
|
39
|
+
- data/crystal/crystal.solution
|
40
|
+
- data/dan-and-matt.sck
|
41
|
+
- data/dan-and-matt.solution
|
42
|
+
- data/howarth/.gitignore
|
43
|
+
- data/howarth/Makefile
|
44
|
+
- data/howarth/mysterious.tar.gz
|
45
|
+
- data/test/Makefile
|
46
|
+
- data/test/adams/Makefile
|
47
|
+
- data/test/adams/adv01.solution
|
48
|
+
- data/test/adams/adv01.transcript
|
49
|
+
- data/test/adams/adv01.transcript.md5
|
50
|
+
- data/test/adams/adv02.solution
|
51
|
+
- data/test/adams/adv02.transcript
|
52
|
+
- data/test/adams/adv02.transcript.md5
|
53
|
+
- data/test/adams/adv04.solution
|
54
|
+
- data/test/adams/adv04.transcript
|
55
|
+
- data/test/adams/adv04.transcript.md5
|
56
|
+
- data/test/crystal.decompile
|
57
|
+
- data/test/crystal.sao
|
58
|
+
- data/test/crystal.save-file
|
59
|
+
- data/test/crystal.save-script
|
60
|
+
- data/test/crystal.sck
|
61
|
+
- data/test/crystal.solution
|
62
|
+
- data/test/crystal.transcript
|
63
|
+
- data/test/t6.pretty-print
|
64
|
+
- data/test/t7.sao
|
65
|
+
- data/test/t7.solution
|
66
|
+
- data/test/t7.transcript
|
67
|
+
- data/tutorial/t1.sck
|
68
|
+
- data/tutorial/t2.sck
|
69
|
+
- data/tutorial/t3.sck
|
70
|
+
- data/tutorial/t4.sck
|
71
|
+
- data/tutorial/t5.sck
|
72
|
+
- data/tutorial/t6.sck
|
73
|
+
- data/tutorial/t7.sck
|
74
|
+
- lib/scottkit/compile.rb
|
75
|
+
- lib/scottkit/decompile.rb
|
76
|
+
- lib/scottkit/game.rb
|
77
|
+
- lib/scottkit/play.rb
|
78
|
+
- notes/Definition
|
79
|
+
- notes/Definition.saved-game
|
80
|
+
- notes/Definition.scottfree-1.14
|
81
|
+
- notes/adventureland-maze
|
82
|
+
- notes/continue-action
|
83
|
+
- test/test_canonicalise.rb
|
84
|
+
- test/test_compile.rb
|
85
|
+
- test/test_decompile.rb
|
86
|
+
- test/test_play.rb
|
87
|
+
- test/test_playadams.rb
|
88
|
+
- test/test_save.rb
|
89
|
+
- test/withio.rb
|
90
|
+
- test/withio_test.rb
|
91
|
+
has_rdoc: true
|
92
|
+
homepage: http://www.miketaylor.org.uk/tech/advent/scottkit/
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options:
|
95
|
+
- --charset=UTF-8
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: "0"
|
103
|
+
version:
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: "0"
|
109
|
+
version:
|
110
|
+
requirements: []
|
111
|
+
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 1.3.1
|
114
|
+
signing_key:
|
115
|
+
specification_version: 2
|
116
|
+
summary: ScottKit is a toolkit for compiling, decompiling and playing adventure games in the Scott Adams format.
|
117
|
+
test_files: []
|
118
|
+
|