two_chainz 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.md +69 -0
- data/lib/two_chainz.rb +2 -5
- data/lib/two_chainz/generator.rb +110 -0
- data/lib/two_chainz/version.rb +3 -0
- data/lib/two_chainz/words_table.rb +79 -0
- metadata +8 -3
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright 2013 Jake Boxer
|
2
|
+
http://jakeboxer.com/
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Two Chainz
|
2
|
+
|
3
|
+
> She got a big booty so I call her Big Booty
|
4
|
+
> Scrr... scrr... wrists moving, cooking, getting to it
|
5
|
+
> I'm in the kitchen, yams everywhere
|
6
|
+
> Just made a jug, I got bands everywhere
|
7
|
+
>
|
8
|
+
– [2 Chainz - Birthday Song](http://rapgenius.com/2-chainz-birthday-song-lyrics) (which was generated entirely with this gem)
|
9
|
+
|
10
|
+
**Two Chainz** is a Ruby gem for generating random sentences with [Markov chains](http://en.wikipedia.org/wiki/Markov_chain).
|
11
|
+
|
12
|
+
## Quickstart
|
13
|
+
|
14
|
+
``` ruby
|
15
|
+
require 'two_chainz'
|
16
|
+
|
17
|
+
generator = TwoChainz::Generator.new
|
18
|
+
|
19
|
+
generator.hear("We just want the credit where it's due")
|
20
|
+
generator.hear("Imma worry bout me, give a fuck about you")
|
21
|
+
generator.hear("Just as a reminder to myself")
|
22
|
+
generator.hear("I wear every single chain even when I'm in the house")
|
23
|
+
|
24
|
+
generator.spit(:words => 12) # => "We just as a fuck chain even when I'm in the credit"
|
25
|
+
```
|
26
|
+
|
27
|
+
## Less important shit
|
28
|
+
|
29
|
+
If you instantiate the generator normally, spitting will be random.
|
30
|
+
|
31
|
+
``` ruby
|
32
|
+
generator = TwoChainz::Generator.new
|
33
|
+
|
34
|
+
generator.hear("I love you I love you")
|
35
|
+
generator.hear("You are alright I guess")
|
36
|
+
|
37
|
+
generator.spit(:words => 2) # => "I guess"
|
38
|
+
generator.spit(:words => 2) # => "You I"
|
39
|
+
```
|
40
|
+
|
41
|
+
You can seed the generator if you want consistency.
|
42
|
+
|
43
|
+
``` ruby
|
44
|
+
generator = TwoChainz::Generator.new(:seed => 3)
|
45
|
+
```
|
46
|
+
|
47
|
+
If you want even more consistency, run it in boring mode. This will always continue the chain the same way: the most common next word is picked every time, and the alphabetically-first word is picked if there's a tie.
|
48
|
+
|
49
|
+
``` ruby
|
50
|
+
generator = TwoChainz::Generator.new(:boring => true)
|
51
|
+
|
52
|
+
generator.hear("I love you I love you I love you")
|
53
|
+
generator.hear("You are alright I guess")
|
54
|
+
|
55
|
+
generator.spit(:words => 2) # => "I love"
|
56
|
+
generator.spit(:words => 2) # => "I love"
|
57
|
+
```
|
58
|
+
|
59
|
+
Instead of specifying how many words you want, you can specify a maximum number of characters. You'll get a sentence that is guaranteed to be that many characters or fewer (and it will usually come pretty close).
|
60
|
+
|
61
|
+
``` ruby
|
62
|
+
generator = TwoChainz::Generator.new
|
63
|
+
|
64
|
+
generator.hear("Once they caught us off-guard")
|
65
|
+
generator.hear("The Mac-10 was in the grass and")
|
66
|
+
generator.hear("I ran like a cheetah with thoughts of an assassin")
|
67
|
+
|
68
|
+
generator.spit(:max_chars => 20) # => "Once they cheetah"
|
69
|
+
```
|
data/lib/two_chainz.rb
CHANGED
@@ -0,0 +1,110 @@
|
|
1
|
+
class TwoChainz::Generator
|
2
|
+
# Public: Create a new TwoChainz::Generator instance.
|
3
|
+
#
|
4
|
+
# options - Hash of options.
|
5
|
+
# :seed - (Optional integer) Seed to use when spitting. If nothing
|
6
|
+
# is provided, Ruby core's Random::new_seed will be used.
|
7
|
+
# :boring - (Optional boolean) Don't do any randomness. Always pick
|
8
|
+
# the most common thing. Mainly for testing. Defaults to
|
9
|
+
# false.
|
10
|
+
#
|
11
|
+
# Returns a TwoChainz::Generator
|
12
|
+
def initialize(options={})
|
13
|
+
@words_table = TwoChainz::WordsTable.new
|
14
|
+
|
15
|
+
unless options[:boring]
|
16
|
+
seed = options[:seed]
|
17
|
+
@random = seed ? Random.new(seed) : Random.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Hear some words and remember them for future spitting.
|
22
|
+
#
|
23
|
+
# words - String of words to hear.
|
24
|
+
#
|
25
|
+
# Returns the number of unique new words the generator heard (integer)
|
26
|
+
def hear(words)
|
27
|
+
heard_words = 0
|
28
|
+
previous_word = nil
|
29
|
+
|
30
|
+
words.scan(/[\w\']+/) do |current_word|
|
31
|
+
# If we haven't heard this word before, increment the newly-heard words
|
32
|
+
# count.
|
33
|
+
heard_words += 1 unless @words_table.include?(current_word)
|
34
|
+
|
35
|
+
# Increment the number of times the current word has been the successor of
|
36
|
+
# the previous word.
|
37
|
+
@words_table.increment(previous_word, current_word)
|
38
|
+
|
39
|
+
previous_word = current_word
|
40
|
+
end
|
41
|
+
|
42
|
+
# Record what the last word was.
|
43
|
+
@words_table.increment(previous_word) if previous_word
|
44
|
+
|
45
|
+
heard_words
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Produce a randomized sentence based on the words that have been
|
49
|
+
# heard.
|
50
|
+
#
|
51
|
+
# options - Hash of options. At least one is required.
|
52
|
+
# :words - (Integer) the number of words to be generated.
|
53
|
+
# :max_chars - (Integer) the maximum number of characters to be
|
54
|
+
# generated.
|
55
|
+
#
|
56
|
+
# Returns a string.
|
57
|
+
def spit(options = {})
|
58
|
+
if @words_table.words.empty?
|
59
|
+
raise StandardError, "The generator hasn't heard anything yet"
|
60
|
+
end
|
61
|
+
|
62
|
+
words = options[:words] && Integer(options[:words])
|
63
|
+
max_chars = options[:max_chars] && Integer(options[:max_chars])
|
64
|
+
|
65
|
+
sentence = []
|
66
|
+
|
67
|
+
if words
|
68
|
+
words.times do |i|
|
69
|
+
previous_word = sentence.last || :beginning
|
70
|
+
sentence << word_after(previous_word)
|
71
|
+
end
|
72
|
+
elsif max_chars
|
73
|
+
sentence_length = -1 # Start at -1 cuz of the first space
|
74
|
+
|
75
|
+
while sentence_length < max_chars
|
76
|
+
previous_word = sentence.last || :beginning
|
77
|
+
word = word_after(previous_word)
|
78
|
+
sentence_length += word.length + 1 # Include space
|
79
|
+
|
80
|
+
sentence << word_after(previous_word) unless sentence_length > max_chars
|
81
|
+
end
|
82
|
+
else
|
83
|
+
raise ArgumentError, "Either :words or :max_chars must be specified"
|
84
|
+
end
|
85
|
+
|
86
|
+
sentence.join(' ')
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Internal: Get a word that comes after the specified one.
|
92
|
+
#
|
93
|
+
# previous_word - The word that will be coming before whichever one we pick.
|
94
|
+
#
|
95
|
+
# Returns a string.
|
96
|
+
def word_after(previous_word)
|
97
|
+
choices = @words_table.words_after(previous_word)
|
98
|
+
|
99
|
+
# Pick the most popular next word.
|
100
|
+
# TODO(jakeboxer): Make this random in non-boring situations.
|
101
|
+
next_word = choices.max_by {|word, count| count}.first
|
102
|
+
|
103
|
+
# If the most popular next word is the sentence ending, pick the
|
104
|
+
# alphabetical first word.
|
105
|
+
# TODO(jakeboxer): Make this random in non-boring situations.
|
106
|
+
next_word = heard_words.sort.first if next_word == :ending
|
107
|
+
|
108
|
+
next_word
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class TwoChainz::WordsTable
|
2
|
+
# Public: Create a new TwoChainz::WordsTable instance.
|
3
|
+
#
|
4
|
+
# Returns a TwoChainz::WordsTable
|
5
|
+
def initialize
|
6
|
+
# The table is a hash. Think of its entries as "rows".
|
7
|
+
# Each row is another hash. Think of its entries as "cells".
|
8
|
+
# Cells default to 0.
|
9
|
+
@table = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Public: Increment the value in the table for the specified pair of words.
|
13
|
+
#
|
14
|
+
# first_word - The word that comes first in the pair. If this is nil, the
|
15
|
+
# second word in the pair will be recorded as the beginning of
|
16
|
+
# a sentence.
|
17
|
+
# second_word - The word that comes last in the pair. If this is nil or
|
18
|
+
# omitted, the first word in the pair will be recorded as the
|
19
|
+
# ending of a sentence.
|
20
|
+
#
|
21
|
+
# Returns the new count of the entry.
|
22
|
+
def increment(first_word=nil, second_word=nil)
|
23
|
+
# Don't do anything if we didn't get a first or second word
|
24
|
+
return 0 unless first_word || second_word
|
25
|
+
|
26
|
+
# Only one of these will run, since we error if both are nil
|
27
|
+
first_word ||= :beginning
|
28
|
+
second_word ||= :ending
|
29
|
+
|
30
|
+
add_row(first_word)
|
31
|
+
add_row(second_word) unless second_word == :ending
|
32
|
+
|
33
|
+
@table[first_word][second_word] += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
# Public: Whether or not the words table has the specified word.
|
37
|
+
#
|
38
|
+
# word - Word to check for inclusion of.
|
39
|
+
#
|
40
|
+
# Returns a boolean
|
41
|
+
def include?(word)
|
42
|
+
@table.keys.include?(word)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Get all the words that have been incremented at least once.
|
46
|
+
#
|
47
|
+
# Returns an array.
|
48
|
+
def words
|
49
|
+
@table.keys - [:beginning]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Public: Get all the words that have come after the specified word. In the
|
53
|
+
# result, the keys are the words and the values are the number of times that
|
54
|
+
# word has appeared after the specified word.
|
55
|
+
#
|
56
|
+
# Can also include the :ending key, which indicates that the sentence ended
|
57
|
+
# after that word.
|
58
|
+
#
|
59
|
+
# word - Word to find all following words for
|
60
|
+
#
|
61
|
+
# Examples
|
62
|
+
#
|
63
|
+
# increment('your', 'love')
|
64
|
+
# increment('your', 'time')
|
65
|
+
# increment('your', 'love')
|
66
|
+
# words_after('your')
|
67
|
+
# # => {"love" => 2, "time" => 1}
|
68
|
+
#
|
69
|
+
# Returns a hash.
|
70
|
+
def words_after(word)
|
71
|
+
Hash[@table[word] || {}]
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def add_row(word)
|
77
|
+
@table[word] ||= Hash.new(0)
|
78
|
+
end
|
79
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: two_chainz
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -18,7 +18,12 @@ executables: []
|
|
18
18
|
extensions: []
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
|
+
- lib/two_chainz/generator.rb
|
22
|
+
- lib/two_chainz/version.rb
|
23
|
+
- lib/two_chainz/words_table.rb
|
21
24
|
- lib/two_chainz.rb
|
25
|
+
- LICENSE
|
26
|
+
- README.md
|
22
27
|
homepage: https://github.com/jakeboxer/two_chainz
|
23
28
|
licenses: []
|
24
29
|
post_install_message:
|
@@ -30,13 +35,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
30
35
|
requirements:
|
31
36
|
- - ! '>='
|
32
37
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
38
|
+
version: 1.9.2
|
34
39
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
35
40
|
none: false
|
36
41
|
requirements:
|
37
42
|
- - ! '>='
|
38
43
|
- !ruby/object:Gem::Version
|
39
|
-
version:
|
44
|
+
version: 1.3.6
|
40
45
|
requirements: []
|
41
46
|
rubyforge_project:
|
42
47
|
rubygems_version: 1.8.23
|