matthewtodd-perquackey 0.4.0 → 0.5.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/CHANGELOG +4 -1
- data/TODO +2 -1
- data/bin/perquackey +3 -2
- data/ext/perquackey/dictionary.c +58 -0
- data/ext/perquackey/extconf.rb +2 -0
- data/lib/perquackey.rb +48 -6
- data/lib/perquackey/server.rb +1 -1
- metadata +8 -19
- data/lib/perquackey/dictionary.rb +0 -57
- data/lib/perquackey/game.rb +0 -15
- data/lib/perquackey/word_table.rb +0 -29
data/CHANGELOG
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
== 0.
|
|
1
|
+
== 0.5.0 -- 2009-05-27
|
|
2
|
+
* Removed dependency on RubyInline. (So I can deploy to Heroku.)
|
|
3
|
+
|
|
4
|
+
== 0.4.0 -- 2009-04-18
|
|
2
5
|
* Removed mongrel dependency.
|
|
3
6
|
* Switched console to use readline instead of irb.
|
|
4
7
|
* Remove stdout usage. Console is the default.
|
data/TODO
CHANGED
data/bin/perquackey
CHANGED
|
@@ -15,8 +15,9 @@ end
|
|
|
15
15
|
|
|
16
16
|
opts.parse! ARGV
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
$:.unshift
|
|
18
|
+
%w(ext lib).each do |dir|
|
|
19
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', dir))
|
|
20
|
+
end
|
|
20
21
|
|
|
21
22
|
case conf.mode
|
|
22
23
|
when :console
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// After finding YAWL, I pretty much lifted this algorithm straight from its
|
|
2
|
+
// anagram.c. It's certainly much faster than the Ruby code I wrote, since
|
|
3
|
+
// it's not making tons of throwaway strings. Fun stuff.
|
|
4
|
+
#include "ruby.h"
|
|
5
|
+
|
|
6
|
+
#define MAX_LENGTH 40
|
|
7
|
+
#define XOUT '@'
|
|
8
|
+
#define FALSE 0
|
|
9
|
+
#define TRUE 1
|
|
10
|
+
|
|
11
|
+
static VALUE mPerquackey;
|
|
12
|
+
static VALUE cDictionary;
|
|
13
|
+
|
|
14
|
+
int spell(char *word, char *letters) {
|
|
15
|
+
static char letter_set[MAX_LENGTH];
|
|
16
|
+
register char *letter;
|
|
17
|
+
|
|
18
|
+
strcpy(letter_set, letters);
|
|
19
|
+
strcat(letter_set, "\n");
|
|
20
|
+
|
|
21
|
+
while (*word) {
|
|
22
|
+
if ((letter = strchr(letter_set, *word++)) != NULL) {
|
|
23
|
+
*letter = XOUT;
|
|
24
|
+
} else {
|
|
25
|
+
return FALSE;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return TRUE;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
VALUE Dictionary_initialize(VALUE self, VALUE filename) {
|
|
33
|
+
rb_iv_set(self, "@filename", filename);
|
|
34
|
+
return self;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
VALUE Dictionary_words(VALUE self, VALUE letters) {
|
|
38
|
+
VALUE words = rb_ary_new();
|
|
39
|
+
|
|
40
|
+
char word[MAX_LENGTH];
|
|
41
|
+
|
|
42
|
+
FILE *file = fopen(RSTRING(rb_iv_get(self, "@filename"))->ptr, "rt");
|
|
43
|
+
while (fgets(word, MAX_LENGTH, file) != NULL) {
|
|
44
|
+
if (spell(word, RSTRING(letters)->ptr)) {
|
|
45
|
+
rb_ary_push(words, rb_str_new(word, strlen(word) - 1));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
fclose(file);
|
|
49
|
+
|
|
50
|
+
return words;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
void Init_dictionary() {
|
|
54
|
+
mPerquackey = rb_define_module("Perquackey");
|
|
55
|
+
cDictionary = rb_define_class_under(mPerquackey, "Dictionary", rb_cObject);
|
|
56
|
+
rb_define_method(cDictionary, "initialize", Dictionary_initialize, 1);
|
|
57
|
+
rb_define_method(cDictionary, "words", Dictionary_words, 1);
|
|
58
|
+
}
|
data/lib/perquackey.rb
CHANGED
|
@@ -1,9 +1,51 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require 'inline'
|
|
1
|
+
require 'perquackey/dictionary'
|
|
3
2
|
|
|
4
3
|
module Perquackey
|
|
5
|
-
|
|
4
|
+
class Game
|
|
5
|
+
YAWL = File.join(File.dirname(__FILE__), '..', 'resources', 'yawl-0.3.2-word.list')
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
def initialize
|
|
8
|
+
@dictionary = Dictionary.new(YAWL)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def words(letters)
|
|
12
|
+
WordTable.new(@dictionary.words(letters).reject { |word| word.length < minimum_length(letters) })
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def minimum_length(letters)
|
|
18
|
+
letters.length > 10 ? 4 : 3
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class WordTable
|
|
23
|
+
include Enumerable
|
|
24
|
+
|
|
25
|
+
def initialize(list=[])
|
|
26
|
+
@table = Hash.new { |h, k| h[k] = [] }
|
|
27
|
+
|
|
28
|
+
list.each do |word|
|
|
29
|
+
@table[word.length] << word
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if @table.empty?
|
|
33
|
+
@headers = []
|
|
34
|
+
@height = 0
|
|
35
|
+
else
|
|
36
|
+
@headers = @table.keys.min .. @table.keys.max
|
|
37
|
+
@height = @table.values.max { |a, b| a.length <=> b.length }.length
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def headers
|
|
42
|
+
@headers
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def each
|
|
46
|
+
@height.times do |row_number|
|
|
47
|
+
yield headers.collect { |length| @table[length][row_number] or ' ' * length}
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/perquackey/server.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: matthewtodd-perquackey
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthew Todd
|
|
@@ -9,19 +9,9 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-
|
|
12
|
+
date: 2009-05-27 00:00:00 -07:00
|
|
13
13
|
default_executable: perquackey
|
|
14
14
|
dependencies:
|
|
15
|
-
- !ruby/object:Gem::Dependency
|
|
16
|
-
name: RubyInline
|
|
17
|
-
type: :runtime
|
|
18
|
-
version_requirement:
|
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
-
requirements:
|
|
21
|
-
- - ">="
|
|
22
|
-
- !ruby/object:Gem::Version
|
|
23
|
-
version: "0"
|
|
24
|
-
version:
|
|
25
15
|
- !ruby/object:Gem::Dependency
|
|
26
16
|
name: camping
|
|
27
17
|
type: :runtime
|
|
@@ -36,8 +26,8 @@ description:
|
|
|
36
26
|
email: matthew.todd@gmail.com
|
|
37
27
|
executables:
|
|
38
28
|
- perquackey
|
|
39
|
-
extensions:
|
|
40
|
-
|
|
29
|
+
extensions:
|
|
30
|
+
- ext/perquackey/extconf.rb
|
|
41
31
|
extra_rdoc_files:
|
|
42
32
|
- README
|
|
43
33
|
- CHANGELOG
|
|
@@ -47,21 +37,20 @@ files:
|
|
|
47
37
|
- CHANGELOG
|
|
48
38
|
- TODO
|
|
49
39
|
- bin/perquackey
|
|
40
|
+
- ext/perquackey/dictionary.c
|
|
41
|
+
- ext/perquackey/extconf.rb
|
|
50
42
|
- lib/perquackey/console.rb
|
|
51
|
-
- lib/perquackey/dictionary.rb
|
|
52
|
-
- lib/perquackey/game.rb
|
|
53
43
|
- lib/perquackey/server.rb
|
|
54
|
-
- lib/perquackey/word_table.rb
|
|
55
44
|
- lib/perquackey.rb
|
|
56
45
|
- resources/yawl-0.3.2-word.list
|
|
57
|
-
has_rdoc:
|
|
46
|
+
has_rdoc: false
|
|
58
47
|
homepage:
|
|
59
48
|
post_install_message:
|
|
60
49
|
rdoc_options:
|
|
61
50
|
- --main
|
|
62
51
|
- README
|
|
63
52
|
- --title
|
|
64
|
-
- perquackey-0.
|
|
53
|
+
- perquackey-0.5.0
|
|
65
54
|
- --inline-source
|
|
66
55
|
- --line-numbers
|
|
67
56
|
require_paths:
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
class Perquackey::Dictionary
|
|
2
|
-
YAWL = File.join(File.dirname(__FILE__), '..', '..', 'resources', 'yawl-0.3.2-word.list')
|
|
3
|
-
|
|
4
|
-
def words(letters)
|
|
5
|
-
c_words(letters, YAWL)
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
# After finding YAWL, I pretty much lifted this algorithm straight from its
|
|
9
|
-
# anagram.c. It's certainly much faster than the Ruby code I wrote, since
|
|
10
|
-
# it's not making tons of throwaway strings. Fun stuff.
|
|
11
|
-
inline do |builder|
|
|
12
|
-
builder.prefix <<-END
|
|
13
|
-
#define MAX_LENGTH 40
|
|
14
|
-
#define XOUT '@'
|
|
15
|
-
#define FALSE 0
|
|
16
|
-
#define TRUE 1
|
|
17
|
-
END
|
|
18
|
-
|
|
19
|
-
builder.c_raw <<-END
|
|
20
|
-
int c_spell(char *word, char *letters) {
|
|
21
|
-
static char letter_set[MAX_LENGTH];
|
|
22
|
-
register char *letter;
|
|
23
|
-
|
|
24
|
-
strcpy(letter_set, letters);
|
|
25
|
-
strcat(letter_set, "\\n");
|
|
26
|
-
|
|
27
|
-
while (*word) {
|
|
28
|
-
if ((letter = strchr(letter_set, *word++)) != NULL) {
|
|
29
|
-
*letter = XOUT;
|
|
30
|
-
} else {
|
|
31
|
-
return FALSE;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return TRUE;
|
|
36
|
-
}
|
|
37
|
-
END
|
|
38
|
-
|
|
39
|
-
builder.c <<-END
|
|
40
|
-
VALUE c_words(VALUE letters, VALUE filename) {
|
|
41
|
-
VALUE words = rb_ary_new();
|
|
42
|
-
|
|
43
|
-
char word[MAX_LENGTH];
|
|
44
|
-
|
|
45
|
-
FILE *file = fopen(RSTRING(filename)->ptr, "rt");
|
|
46
|
-
while (fgets(word, MAX_LENGTH, file) != NULL) {
|
|
47
|
-
if (c_spell(word, RSTRING(letters)->ptr)) {
|
|
48
|
-
rb_ary_push(words, rb_str_new(word, strlen(word) - 1));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
fclose(file);
|
|
52
|
-
|
|
53
|
-
return words;
|
|
54
|
-
}
|
|
55
|
-
END
|
|
56
|
-
end
|
|
57
|
-
end
|
data/lib/perquackey/game.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
class Perquackey::Game
|
|
2
|
-
def initialize
|
|
3
|
-
@dictionary = Perquackey::Dictionary.new
|
|
4
|
-
end
|
|
5
|
-
|
|
6
|
-
def words(letters)
|
|
7
|
-
Perquackey::WordTable.new(@dictionary.words(letters).reject { |word| word.length < minimum_length(letters) })
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
def minimum_length(letters)
|
|
13
|
-
letters.length > 10 ? 4 : 3
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
class Perquackey::WordTable
|
|
2
|
-
include Enumerable
|
|
3
|
-
|
|
4
|
-
def initialize(list=[])
|
|
5
|
-
@table = Hash.new { |h, k| h[k] = [] }
|
|
6
|
-
|
|
7
|
-
list.each do |word|
|
|
8
|
-
@table[word.length] << word
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
if @table.empty?
|
|
12
|
-
@headers = []
|
|
13
|
-
@height = 0
|
|
14
|
-
else
|
|
15
|
-
@headers = @table.keys.min .. @table.keys.max
|
|
16
|
-
@height = @table.values.max { |a, b| a.length <=> b.length }.length
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def headers
|
|
21
|
-
@headers
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def each
|
|
25
|
-
@height.times do |row_number|
|
|
26
|
-
yield headers.collect { |length| @table[length][row_number] or ' ' * length}
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|