acrosslite 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.rdoc +88 -0
- data/Rakefile +56 -0
- data/lib/acrosslite.rb +263 -0
- data/lib/entry.rb +9 -0
- data/spec/acrosslite_spec.rb +102 -0
- data/spec/files/crnet100306.puz +0 -0
- data/spec/files/halloween2009.puz +0 -0
- data/spec/files/tmcal100306.puz +0 -0
- data/spec/files/xp100306.puz +0 -0
- data/spec/files/ydx100515.puz +0 -0
- metadata +88 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2010 Samuel Mullen
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
= Acrosslite
|
2
|
+
|
3
|
+
http://github.com/samullen/acrosslite
|
4
|
+
|
5
|
+
A Ruby library for parsing Across Lite crossword puzzle (.puz) files.
|
6
|
+
|
7
|
+
The Across Lite format is probably the most popular format for encoding
|
8
|
+
crossword puzzles. This library in its current incarnation provides a means for
|
9
|
+
retrieving the encoded crossword information from that format. In the future I
|
10
|
+
may take a go at building an encoder, but there are some legal issues around
|
11
|
+
doing such.
|
12
|
+
|
13
|
+
For more information about acrosslite, go to http://litsoft.com.
|
14
|
+
|
15
|
+
== Installation
|
16
|
+
|
17
|
+
The acrosslite gem is hosted on RubyGems (http://rubygems.org).
|
18
|
+
|
19
|
+
== Getting Started
|
20
|
+
|
21
|
+
Instantiation of the library can be done in one of two ways: 1) passing in the full path to a file; 2) by passing in the puzzle blob.
|
22
|
+
|
23
|
+
require 'acrosslite'
|
24
|
+
|
25
|
+
ac = Acrosslite.new(:filepath => "/path/to/the/puzzle/file.puz")
|
26
|
+
|
27
|
+
-- Or --
|
28
|
+
|
29
|
+
blob = open("/path/to/the/puzzle/file.puz", "r").read
|
30
|
+
ac = Acrosslite.new(:content => blob)
|
31
|
+
|
32
|
+
Useful information about the puzzle can be retrieved with a handful of method
|
33
|
+
calls.
|
34
|
+
|
35
|
+
=== Puzzle Meta
|
36
|
+
|
37
|
+
ac.title # -> Title of the puzzle
|
38
|
+
ac.author # -> Author of the puzzle
|
39
|
+
ac.copyright # -> Puzzle Copyright
|
40
|
+
|
41
|
+
=== Puzzle Content
|
42
|
+
|
43
|
+
ac.diagram # -> two-dimensional matrix of the diagram
|
44
|
+
ac.solution # -> two-dimensional matrix of the solution
|
45
|
+
|
46
|
+
ac.rows # -> Array of Acrosslite::Entry objects
|
47
|
+
ac.columns # -> Array of Acrosslite::Entry objects
|
48
|
+
|
49
|
+
Acrosslite::Entry objects are broken down thusly:
|
50
|
+
|
51
|
+
ac.direction - The direction the answer goes (across, down)
|
52
|
+
ac.clue - The clue to provide an answer for
|
53
|
+
ac.clue_number - Clue number represented by the little number in a crossword cell
|
54
|
+
ac.row - What row the answer begins on (zero-based).
|
55
|
+
ac.column - What column the answer begins on (zero-based).
|
56
|
+
ac.length - The length of the answer
|
57
|
+
ac.cell_number - The "physical" cell the answer begins on
|
58
|
+
ac.answer - The answer
|
59
|
+
|
60
|
+
== Other
|
61
|
+
|
62
|
+
US Crossword Rules:
|
63
|
+
http://www.maa.org/editorial/mathgames/mathgames_05_10_04.html
|
64
|
+
|
65
|
+
The rules for American crosswords are as follows:
|
66
|
+
|
67
|
+
1. The pattern of black-and-white squares must be symmetrical. Generally this rule means that if you turn the grid upside-down, the pattern will look the same as it does right-side-up.
|
68
|
+
2. Do not use too many black squares. In the old days of puzzles, black squares were not allowed to occupy more than 16% of a grid. Nowadays there is no strict limit, in order to allow maximum flexibility for the placement of theme entries. Still, "cheater" black squares (ones that do not affect the number of words in the puzzle, but are added to make constructing easier) should be kept to a minimum, and large clumps of black squares anywhere in a grid are strongly discouraged.
|
69
|
+
3. Do not use unkeyed letters (letters that appear in only one word across or down). In fairness to solvers, every letter has to be appear in both an Across and a Down word.
|
70
|
+
4. Do not use two-letter words. The minimum word length is three letters.
|
71
|
+
5. The grid must have all-over interlock. In other words, the black squares may not cut the grid up into separate pieces. A solver, theoretically, should be able to able to proceed from any section of the grid to any other without having to stop and start over.
|
72
|
+
6. Long theme entries must be symmetrically placed. If there is a major theme entry three rows down from the top of the grid, for instance, then there must be another theme entry in the same position three rows up from the bottom. Also, as a general rule, no nontheme entry should be longer than any theme entry.
|
73
|
+
7. Do not repeat words in the grid.
|
74
|
+
8. Do not make up words and phrases. Every answer must have a reference or else be in common use in everyday speech or writing.
|
75
|
+
9. (Modern rule) The vocabulary in a crossword must be lively and have very little obscurity.
|
76
|
+
|
77
|
+
== Acknowledgements
|
78
|
+
|
79
|
+
* Doug Sparling who created the perl Convert-AcrossLite library (http://cpansearch.perl.org/src/DSPARLING/Convert-AcrossLite-0.10/README). I also credit him with getting me into Ruby and getting me excited about programming again.
|
80
|
+
* Bob Newell (http://www.gtoal.com/wordgames/gene/AcrossLite) who originally decyphered the Acrosslite format to begin with.
|
81
|
+
|
82
|
+
== Author
|
83
|
+
|
84
|
+
Samuel Mullen <samullen@gmail.com>
|
85
|
+
|
86
|
+
== Copyright
|
87
|
+
|
88
|
+
Copyright(c) 2010 Samuel Mullen (samullen). See LICENSE for details
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'acrosslite'
|
6
|
+
|
7
|
+
lib_dir = File.expand_path('lib')
|
8
|
+
spec_dir = File.expand_path('spec')
|
9
|
+
|
10
|
+
gem_spec = Gem::Specification.new do |s|
|
11
|
+
s.name = "acrosslite"
|
12
|
+
s.version = Acrosslite::VERSION
|
13
|
+
s.authors = ["Samuel Mullen"]
|
14
|
+
s.email = "samullen@gmail.com"
|
15
|
+
s.homepage = "http://github.com/samullen/acrosslite"
|
16
|
+
s.summary = "A Ruby library for parsing Across Lite puzzle (.puz) files"
|
17
|
+
s.description = false
|
18
|
+
s.test_files = Dir['spec/**/*']
|
19
|
+
s.add_development_dependency "rspec"
|
20
|
+
s.files = [
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"lib/acrosslite.rb",
|
25
|
+
"lib/entry.rb"
|
26
|
+
] + s.test_files
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'spec/rake/spectask'
|
31
|
+
rescue LoadError
|
32
|
+
task :spec do
|
33
|
+
$stderr.puts '`gem install rspec` to run specs'
|
34
|
+
end
|
35
|
+
else
|
36
|
+
desc "Run specs"
|
37
|
+
Spec::Rake::SpecTask.new do |t|
|
38
|
+
t.spec_files = Dir['spec/**/*.rb']
|
39
|
+
t.spec_opts = %w(-fs --color)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Rake::GemPackageTask.new(gem_spec) do |pkg|
|
44
|
+
pkg.need_zip = false
|
45
|
+
pkg.need_tar = false
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Install the gem locally"
|
49
|
+
task :install => [:spec, :gem] do
|
50
|
+
sh %{gem install pkg/#{gem_spec.name}-#{gem_spec.version}}
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Remove the pkg directory and all of its contents."
|
54
|
+
task :clean => :clobber_package
|
55
|
+
|
56
|
+
task :default => [:spec, :gem]
|
data/lib/acrosslite.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), 'entry')
|
4
|
+
|
5
|
+
class Acrosslite
|
6
|
+
attr_reader :across, :down, :solution, :diagram, :copyright, :title, :author,
|
7
|
+
:filepath
|
8
|
+
|
9
|
+
VERSION = '0.2.0'
|
10
|
+
|
11
|
+
ACROSSLITE = 2
|
12
|
+
ROWS = 44
|
13
|
+
COLUMNS = 45
|
14
|
+
SOLUTION = 52
|
15
|
+
|
16
|
+
DEFAULT_OPTIONS = {
|
17
|
+
:filepath => nil,
|
18
|
+
:content => nil,
|
19
|
+
}
|
20
|
+
|
21
|
+
def initialize(*args)
|
22
|
+
opts = {}
|
23
|
+
|
24
|
+
case
|
25
|
+
when args.length == 0 then
|
26
|
+
when args.length == 1 && args[0].class == Hash then
|
27
|
+
arg = args.shift
|
28
|
+
|
29
|
+
if arg.class == Hash
|
30
|
+
opts = arg
|
31
|
+
end
|
32
|
+
else
|
33
|
+
raise ArgumentError, "new() expects hash or hashref as argument"
|
34
|
+
end
|
35
|
+
|
36
|
+
opts = DEFAULT_OPTIONS.merge opts
|
37
|
+
|
38
|
+
@filepath = opts[:filepath]
|
39
|
+
@content = opts[:content] || content
|
40
|
+
|
41
|
+
@content_io = StringIO.new @content
|
42
|
+
|
43
|
+
@across = Array.new
|
44
|
+
@down = Array.new
|
45
|
+
@layout = Array.new
|
46
|
+
@solution = Array.new
|
47
|
+
@diagram = Array.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def content
|
51
|
+
@content ||= read_puzzle
|
52
|
+
end
|
53
|
+
|
54
|
+
def rows
|
55
|
+
unless @rows
|
56
|
+
@content_io.seek(ROWS)
|
57
|
+
@rows = @content_io.read(1).unpack('C').first
|
58
|
+
end
|
59
|
+
@rows
|
60
|
+
end
|
61
|
+
|
62
|
+
def columns
|
63
|
+
unless @columns
|
64
|
+
@content_io.seek(COLUMNS)
|
65
|
+
@columns = @content_io.read(1).unpack('C').first
|
66
|
+
end
|
67
|
+
@columns
|
68
|
+
end
|
69
|
+
|
70
|
+
def solution
|
71
|
+
width = columns
|
72
|
+
|
73
|
+
# if @solution.empty?
|
74
|
+
unless @solution.size == rows
|
75
|
+
@content_io.seek(SOLUTION)
|
76
|
+
|
77
|
+
rows.times do |r|
|
78
|
+
@solution << @content_io.read(width).unpack("C#{width}").map {|c| c.chr}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
@solution
|
82
|
+
end
|
83
|
+
|
84
|
+
def diagram
|
85
|
+
width = columns
|
86
|
+
|
87
|
+
if @diagram.empty?
|
88
|
+
@content_io.seek(SOLUTION + rows * width)
|
89
|
+
|
90
|
+
rows.times do |r|
|
91
|
+
@diagram << @content_io.read(width).unpack("C#{width}").map {|c| c.chr}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
@diagram
|
95
|
+
end
|
96
|
+
|
97
|
+
def area
|
98
|
+
rows * columns
|
99
|
+
end
|
100
|
+
|
101
|
+
def across
|
102
|
+
if @across.empty?
|
103
|
+
parse
|
104
|
+
end
|
105
|
+
|
106
|
+
@across
|
107
|
+
end
|
108
|
+
|
109
|
+
def down
|
110
|
+
if @down.empty?
|
111
|
+
parse
|
112
|
+
end
|
113
|
+
|
114
|
+
@down
|
115
|
+
end
|
116
|
+
|
117
|
+
def title
|
118
|
+
unless @title
|
119
|
+
parse
|
120
|
+
end
|
121
|
+
|
122
|
+
@title
|
123
|
+
end
|
124
|
+
|
125
|
+
def author
|
126
|
+
unless @author
|
127
|
+
parse
|
128
|
+
end
|
129
|
+
|
130
|
+
@author
|
131
|
+
end
|
132
|
+
|
133
|
+
def copyright
|
134
|
+
unless @copyright
|
135
|
+
parse
|
136
|
+
end
|
137
|
+
|
138
|
+
@copyright
|
139
|
+
end
|
140
|
+
|
141
|
+
=begin rdoc
|
142
|
+
If a filehandle or filepath were provided, open reads in the file's contents
|
143
|
+
into the content attribute which is then used for parsing.
|
144
|
+
|
145
|
+
open must be called prior to parsing if content has not already been provided.
|
146
|
+
|
147
|
+
=end
|
148
|
+
def read_puzzle(filepath=nil)
|
149
|
+
filepath ||= @filepath
|
150
|
+
raise unless filepath
|
151
|
+
@content = open(@filepath).read
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
=begin rdoc
|
157
|
+
=end
|
158
|
+
def next_field
|
159
|
+
string = String.new
|
160
|
+
|
161
|
+
while (c = @content_io.getc.chr) != "\0"
|
162
|
+
string += c
|
163
|
+
end
|
164
|
+
|
165
|
+
return string
|
166
|
+
end
|
167
|
+
|
168
|
+
def content_io
|
169
|
+
@content_io ||= StringIO.new @content
|
170
|
+
end
|
171
|
+
|
172
|
+
=begin rdoc
|
173
|
+
The parse method takes the puzzle loaded into content and breaks it out into the
|
174
|
+
following attributes: rows, columns, solution, diagram, title, author, copyright, across, and down.
|
175
|
+
=end
|
176
|
+
|
177
|
+
def parse
|
178
|
+
clues = Array.new
|
179
|
+
|
180
|
+
@content_io.seek(SOLUTION + area + area)
|
181
|
+
|
182
|
+
@title = next_field
|
183
|
+
@author = next_field
|
184
|
+
@copyright = next_field
|
185
|
+
|
186
|
+
#----- build clues array -----#
|
187
|
+
until @content_io.eof? do
|
188
|
+
clues << next_field
|
189
|
+
end
|
190
|
+
|
191
|
+
#----- determine answers -----#
|
192
|
+
across_clue = down_clue = 1 # clue_number: incremented only in "down" area
|
193
|
+
|
194
|
+
0.upto(rows - 1) do |r|
|
195
|
+
0.upto(columns - 1) do |c|
|
196
|
+
next if solution[r][c] =~ /[.:]/
|
197
|
+
|
198
|
+
if c - 1 < 0 || solution[r][c - 1] == "."
|
199
|
+
entry = Acrosslite::Entry.new
|
200
|
+
answer = ''
|
201
|
+
|
202
|
+
c.upto(columns - 1) do |cc|
|
203
|
+
char = solution[r][cc]
|
204
|
+
|
205
|
+
if char != '.'
|
206
|
+
answer += char
|
207
|
+
end
|
208
|
+
|
209
|
+
if char == "." || cc + 1 >= columns
|
210
|
+
entry.direction = "across"
|
211
|
+
entry.clue = clues.shift
|
212
|
+
entry.answer = answer
|
213
|
+
entry.clue_number = across_clue
|
214
|
+
entry.row = r
|
215
|
+
entry.column = c
|
216
|
+
entry.length = answer.size
|
217
|
+
entry.cell_number = r * columns + c + 1
|
218
|
+
|
219
|
+
@across << entry
|
220
|
+
across_clue += 1
|
221
|
+
break
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
if r - 1 < 0 || solution[r - 1][c] == "."
|
227
|
+
entry = Acrosslite::Entry.new
|
228
|
+
answer = ''
|
229
|
+
|
230
|
+
r.upto(rows - 1) do |rr|
|
231
|
+
char = solution[rr][c]
|
232
|
+
|
233
|
+
if char != '.'
|
234
|
+
answer += char
|
235
|
+
end
|
236
|
+
|
237
|
+
if char == "." || rr + 1 >= rows
|
238
|
+
entry.direction = "down"
|
239
|
+
entry.clue = clues.shift
|
240
|
+
entry.answer = answer
|
241
|
+
entry.clue_number = down_clue
|
242
|
+
entry.row = r
|
243
|
+
entry.column = c
|
244
|
+
entry.length = answer.size
|
245
|
+
entry.cell_number = r * columns + c + 1
|
246
|
+
|
247
|
+
@down << entry
|
248
|
+
down_clue += 1
|
249
|
+
break
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
if across_clue > down_clue
|
255
|
+
down_clue = across_clue
|
256
|
+
else
|
257
|
+
across_clue = down_clue
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
data/lib/entry.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib','acrosslite')
|
2
|
+
|
3
|
+
describe Acrosslite do
|
4
|
+
before(:all) do
|
5
|
+
basedir = File.dirname(__FILE__)
|
6
|
+
@example_files = Hash.new
|
7
|
+
|
8
|
+
@example_files[:halloween] = File.join(basedir, "files/halloween2009.puz")
|
9
|
+
@example_files[:crnet] = File.join(basedir, "files/crnet100306.puz")
|
10
|
+
@example_files[:tmcal] = File.join(basedir, "files/tmcal100306.puz")
|
11
|
+
@example_files[:xp] = File.join(basedir, "files/xp100306.puz")
|
12
|
+
@example_files[:ydx] = File.join(basedir, "files/ydx100515.puz")
|
13
|
+
end
|
14
|
+
|
15
|
+
# before(:each) do
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# after(:each) do
|
19
|
+
# end
|
20
|
+
|
21
|
+
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
|
22
|
+
# Builder Tests
|
23
|
+
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
|
24
|
+
|
25
|
+
it "should instantiate the puzzle with passing of file" do
|
26
|
+
ac = Acrosslite.new(:filepath => @example_files[:halloween])
|
27
|
+
ac.should be_an_instance_of Acrosslite
|
28
|
+
ac.filepath.should == @example_files[:halloween]
|
29
|
+
ac.content.should == File.open(@example_files[:halloween]).read
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should instantiate the puzzle with passing of an acrosslite blob" do
|
33
|
+
ac = Acrosslite.new(:content => File.open(@example_files[:halloween]).read)
|
34
|
+
ac.should be_an_instance_of Acrosslite
|
35
|
+
ac.content.should == File.open(@example_files[:halloween]).read
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should parse dimensions" do
|
39
|
+
ac = Acrosslite.new(:filepath => @example_files[:tmcal])
|
40
|
+
ac.rows.should == 15
|
41
|
+
ac.columns.should == 15
|
42
|
+
ac.area.should == 15 * 15
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should parse solution and diagram" do
|
46
|
+
ac = Acrosslite.new(:filepath => @example_files[:crnet])
|
47
|
+
ac.solution.join.should == 'PASSED.MAMACASSLEANER.AMICABLYURBANA.FIGUREONCARRYWEIGHT.TVAKTEL.BLOAT.STEPYES.GLASS.COINS...KYOTO.TOWNIEMCENROE.RAREGASALLIED.RAKED...NESTS.HADES.PEWDAIS.REPOS.MUNIARN.FORINSTANCELOOKATME.IONIANAUREVOIR.CROSSESTEWARTS.KOSHER'
|
48
|
+
ac.diagram.join.should == '------.--------------.--------------.-------------------.-------.-----.-------.-----.-----...-----.-------------.-------------.-----...-----.-----.-------.-----.-------.-------------------.--------------.--------------.------'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should retrieve meta data about the puzzle" do
|
52
|
+
ac = Acrosslite.new(:filepath => @example_files[:tmcal])
|
53
|
+
ac.title.should == 'LA Times, Sat, Mar 6, 2010'
|
54
|
+
ac.author.should == 'Barry C. Silk / Ed. Rich Norris'
|
55
|
+
# ac.copyright.should == "© 2010 Tribune Media Services, Inc."
|
56
|
+
|
57
|
+
ac = Acrosslite.new(:filepath => @example_files[:crnet])
|
58
|
+
ac.title.should == '03/06/10 SATURDAY STUMPER'
|
59
|
+
ac.author.should == 'Merle Baker , edited by Stanley Newman'
|
60
|
+
# ac.copyright.should == "© Copyright 2010 Stanley Newman, Distributed by Creators Syndicate, Inc."
|
61
|
+
|
62
|
+
ac = Acrosslite.new(:filepath => @example_files[:xp])
|
63
|
+
ac.title.should == ''
|
64
|
+
ac.author.should == ''
|
65
|
+
ac.copyright.should == ''
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should retrieve the data for each of the entries" do
|
69
|
+
ac = Acrosslite.new(:filepath => @example_files[:halloween])
|
70
|
+
ac.across.first.clue.should == "Item sought by kids in costumes"
|
71
|
+
ac.across.first.answer.should == "CANDY"
|
72
|
+
ac.across.first.clue_number.should == 1
|
73
|
+
ac.across.first.row.should == 0
|
74
|
+
ac.across.first.column.should == 0
|
75
|
+
ac.across.first.length.should == 5
|
76
|
+
ac.across.first.cell_number.should == 1
|
77
|
+
|
78
|
+
ac.across.last.clue.should == "Has to have"
|
79
|
+
ac.across.last.answer.should == "NEEDS"
|
80
|
+
ac.across.last.clue_number.should == 73
|
81
|
+
ac.across.last.row.should == 14
|
82
|
+
ac.across.last.column.should == 10
|
83
|
+
ac.across.last.length.should == 5
|
84
|
+
ac.across.last.cell_number.should == 221
|
85
|
+
|
86
|
+
ac.down.first.clue.should == "Slept under the stars"
|
87
|
+
ac.down.first.answer.should == "CAMPED"
|
88
|
+
ac.down.first.clue_number.should == 1
|
89
|
+
ac.down.first.row.should == 0
|
90
|
+
ac.down.first.column.should == 0
|
91
|
+
ac.down.first.length.should == 6
|
92
|
+
ac.down.first.cell_number.should == 1
|
93
|
+
|
94
|
+
ac.down.last.clue.should == "Take in slowly"
|
95
|
+
ac.down.last.answer.should == "SIP"
|
96
|
+
ac.down.last.clue_number.should == 66
|
97
|
+
ac.down.last.row.should == 12
|
98
|
+
ac.down.last.column.should == 6
|
99
|
+
ac.down.last.length.should == 3
|
100
|
+
ac.down.last.cell_number.should == 187
|
101
|
+
end
|
102
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acrosslite
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Samuel Mullen
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-25 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
description: "false"
|
33
|
+
email: samullen@gmail.com
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files: []
|
39
|
+
|
40
|
+
files:
|
41
|
+
- LICENSE
|
42
|
+
- README.rdoc
|
43
|
+
- Rakefile
|
44
|
+
- lib/acrosslite.rb
|
45
|
+
- lib/entry.rb
|
46
|
+
- spec/acrosslite_spec.rb
|
47
|
+
- spec/files/tmcal100306.puz
|
48
|
+
- spec/files/crnet100306.puz
|
49
|
+
- spec/files/halloween2009.puz
|
50
|
+
- spec/files/ydx100515.puz
|
51
|
+
- spec/files/xp100306.puz
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://github.com/samullen/acrosslite
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.6
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: A Ruby library for parsing Across Lite puzzle (.puz) files
|
82
|
+
test_files:
|
83
|
+
- spec/acrosslite_spec.rb
|
84
|
+
- spec/files/tmcal100306.puz
|
85
|
+
- spec/files/crnet100306.puz
|
86
|
+
- spec/files/halloween2009.puz
|
87
|
+
- spec/files/ydx100515.puz
|
88
|
+
- spec/files/xp100306.puz
|