megahal 0.4.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af7cea408577bbc60da09d9ea222afc32fb0624c
4
+ data.tar.gz: f27c277b75969c4a7bcd94fe6a0de4c56fa68988
5
+ SHA512:
6
+ metadata.gz: f62986be611257ee349cbc4f48d4e4e170ba94c0853240f592bfdcae63601b7826ac6764391c06085730885e1c415b2040fe0b6d78e7711c1d92be29f290c491
7
+ data.tar.gz: c1ac5d103372d12a53c8e4d3d8173537fba9730241fc1f0b65cd8db3d99e8e04a083507c077b89e8a6a3b02017fc2c9f7f75d1f86433f0c567020d7e038808f8
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sooth', '~> 1.0'
4
+ gem 'highline', '~> 1.6'
5
+ gem 'hodel_3000_compliant_logger', '~> 0.1'
6
+ gem 'ruby-progressbar', '~> 1.7'
7
+ gem 'rubyzip', '~> 1.1'
8
+ gem 'cld', '~> 0.7'
9
+
10
+ group :development do
11
+ gem 'rspec', '~> 3.1'
12
+ gem 'yard', '~> 0.8'
13
+ gem 'rdoc', '~> 4.1'
14
+ gem 'bundler', '~> 1.7'
15
+ gem 'jeweler', '~> 2.0'
16
+ gem 'simplecov', '~> 0.9'
17
+ gem 'byebug', '~> 3.5'
18
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,100 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.3.6)
5
+ builder (3.2.2)
6
+ byebug (3.5.1)
7
+ columnize (~> 0.8)
8
+ debugger-linecache (~> 1.2)
9
+ slop (~> 3.6)
10
+ cld (0.7.0)
11
+ ffi
12
+ columnize (0.9.0)
13
+ debugger-linecache (1.2.0)
14
+ descendants_tracker (0.0.4)
15
+ thread_safe (~> 0.3, >= 0.3.1)
16
+ diff-lcs (1.2.5)
17
+ docile (1.1.5)
18
+ faraday (0.9.0)
19
+ multipart-post (>= 1.2, < 3)
20
+ ffi (1.9.6)
21
+ git (1.2.8)
22
+ github_api (0.12.2)
23
+ addressable (~> 2.3)
24
+ descendants_tracker (~> 0.0.4)
25
+ faraday (~> 0.8, < 0.10)
26
+ hashie (>= 3.3)
27
+ multi_json (>= 1.7.5, < 2.0)
28
+ nokogiri (~> 1.6.3)
29
+ oauth2
30
+ hashie (3.3.2)
31
+ highline (1.6.21)
32
+ hodel_3000_compliant_logger (0.1.1)
33
+ jeweler (2.0.1)
34
+ builder
35
+ bundler (>= 1.0)
36
+ git (>= 1.2.5)
37
+ github_api
38
+ highline (>= 1.6.15)
39
+ nokogiri (>= 1.5.10)
40
+ rake
41
+ rdoc
42
+ json (1.8.1)
43
+ jwt (1.2.0)
44
+ mini_portile (0.6.1)
45
+ multi_json (1.10.1)
46
+ multi_xml (0.5.5)
47
+ multipart-post (2.0.0)
48
+ nokogiri (1.6.5)
49
+ mini_portile (~> 0.6.0)
50
+ oauth2 (1.0.0)
51
+ faraday (>= 0.8, < 0.10)
52
+ jwt (~> 1.0)
53
+ multi_json (~> 1.3)
54
+ multi_xml (~> 0.5)
55
+ rack (~> 1.2)
56
+ rack (1.5.2)
57
+ rake (10.4.2)
58
+ rdoc (4.2.0)
59
+ json (~> 1.4)
60
+ rspec (3.1.0)
61
+ rspec-core (~> 3.1.0)
62
+ rspec-expectations (~> 3.1.0)
63
+ rspec-mocks (~> 3.1.0)
64
+ rspec-core (3.1.7)
65
+ rspec-support (~> 3.1.0)
66
+ rspec-expectations (3.1.2)
67
+ diff-lcs (>= 1.2.0, < 2.0)
68
+ rspec-support (~> 3.1.0)
69
+ rspec-mocks (3.1.3)
70
+ rspec-support (~> 3.1.0)
71
+ rspec-support (3.1.2)
72
+ ruby-progressbar (1.7.0)
73
+ rubyzip (1.1.6)
74
+ simplecov (0.9.1)
75
+ docile (~> 1.1.0)
76
+ multi_json (~> 1.0)
77
+ simplecov-html (~> 0.8.0)
78
+ simplecov-html (0.8.0)
79
+ slop (3.6.0)
80
+ sooth (1.0.0)
81
+ thread_safe (0.3.4)
82
+ yard (0.8.7.6)
83
+
84
+ PLATFORMS
85
+ ruby
86
+
87
+ DEPENDENCIES
88
+ bundler (~> 1.7)
89
+ byebug (~> 3.5)
90
+ cld (~> 0.7)
91
+ highline (~> 1.6)
92
+ hodel_3000_compliant_logger (~> 0.1)
93
+ jeweler (~> 2.0)
94
+ rdoc (~> 4.1)
95
+ rspec (~> 3.1)
96
+ ruby-progressbar (~> 1.7)
97
+ rubyzip (~> 1.1)
98
+ simplecov (~> 0.9)
99
+ sooth (~> 1.0)
100
+ yard (~> 0.8)
data/README.md ADDED
@@ -0,0 +1,187 @@
1
+ [![Gratipay](https://img.shields.io/gratipay/Kranzky.svg)](https://gratipay.com/Kranzky/)
2
+ [![Gem Version](https://badge.fury.io/rb/megahal.svg)](http://badge.fury.io/rb/megahal)
3
+ [![Dependency Status](https://gemnasium.com/jasonhutchens/megahal.png)](https://gemnasium.com/jasonhutchens/megahal)
4
+ [![Build Status](https://semaphoreapp.com/api/v1/projects/6889bf33-e547-4200-a4fb-66b339a83d82/307128/shields_badge.svg)](https://semaphoreapp.com/jasonhutchens/megahal)
5
+ [![Code Climate](https://codeclimate.com/github/jasonhutchens/megahal.png)](https://codeclimate.com/github/jasonhutchens/megahal)
6
+
7
+ MegaHAL
8
+ =======
9
+
10
+ MegaHAL is a learning chatterbot.
11
+
12
+ You can run it as a command-line application, or embed it in your own
13
+ application and make calls to it via a simple
14
+ [API](http://rubydoc.info/github/jasonhutchens/megahal/master/frames).
15
+
16
+ It is built on top of [Sooth](https://github.com/jasonhutchens/sooth), a simple
17
+ stochastic predictive model.
18
+
19
+ This new version of MegaHAL was written in homage of the original, which I wrote
20
+ around twenty years ago. As these things go, the Ruby version is a fraction of
21
+ the size of the [original C version](https://github.com/pteichman/megahal/blob/master/Megahal/megahal.c).
22
+
23
+ The oldest record I have of MegaHAL is a USENET post from April 30, 1995,
24
+ which is reproduced at the bottom of this README.
25
+
26
+ Getting Started
27
+ ---------------
28
+
29
+ Running the command-line version of MegaHAL is simple:
30
+
31
+ ```
32
+ > gem install megahal
33
+ > megahal
34
+ +----------------------------------------------------------+
35
+ | |
36
+ | # # ###### #### ## # # ## # |
37
+ | ## ## # # # # # # # # # # |
38
+ | # ## # ##### # # # ###### # # # |
39
+ | # # # # ### ###### # # ###### # |
40
+ | # # # # # # # # # # # # |
41
+ | # # ###### #### # # # # # # ###### |
42
+ | |
43
+ | Type "/help" for options and "/quit" to terminate. |
44
+ +----------------------------------------------------------+
45
+
46
+ Greetings and salutations to thee and thine.
47
+ >
48
+ ```
49
+
50
+ If you type `/help` at the prompt, you'll be presented with a menu:
51
+
52
+ ```
53
+ 1. cancel
54
+ 2. reset
55
+ 3. brain
56
+ 4. train
57
+ 5. load
58
+ 6. save
59
+ 7. ignore
60
+ 8. quit
61
+ ```
62
+
63
+ Make a selection by typing the number or the name of the menu item:
64
+
65
+ 1. `cancel`: go back without making a selection
66
+ 1. `reset`: clear MegaHAL's brain, restoring it to a blank slate
67
+ 1. `brain`: choose from a number of existing personalities (see below)
68
+ 1. `train`: give MegaHAL a plain text file to learn from
69
+ 1. `load`: load a previously saved brain from a file
70
+ 1. `save`: save the current MegaHAL brain to a file
71
+ 1. `ignore`: prevent MegaHAL from learning from user input; this is good if you want to chat without introducing changes to the brain
72
+ 1. `quit`: immediately exit MegaHAL, losing any unsaved changes
73
+
74
+ If you select the `brain` menu item above, you'll be presented with a list of
75
+ pre-existing MegaHAL brains to choose from:
76
+
77
+ 1. `cancel`: go back without making a selection
78
+ 2. `default`: the personality used for the 1998 Loebner competition
79
+ 3. `aliens`: Bishop from Aliens!
80
+ 4. `ferris`: Mr. Ferris Bueller himself! Bueller! Bueller!
81
+ 5. `pepys`: Samuel Pepys, taken from the @samuelpepys Twitter feed
82
+ 6. `pulp`: Marsellus Wallace from Pulp Fiction!
83
+ 7. `sherlock`: Sherlock Holmes, with quotes taken from all the books
84
+ 8. `startrek`: Data from Star Trek (thanks to mbaker)
85
+ 9. `starwars`: Threepio from the Star Wars Trilogy!
86
+
87
+ Example
88
+ -------
89
+
90
+ Here is an example conversation transcript, taken directly from the `megahal.log` file (which is written each time you interact with MegaHAL):
91
+
92
+ ```
93
+ (brain changed to sherlock holmes)
94
+ MH10: On the strange coincidences, the plannings, the cross-purposes, the wonderful chains of events, working through generations, and leading to the composition of a woman.
95
+ USER: Who are you?
96
+ MH10: You could never guess how I employed my morning, or what I ended by doing.
97
+ USER: Did you play a game?
98
+ MH10: Come, Watson, come! The game is afoot.
99
+ USER: How exciting!
100
+ MH10: Isn't it gorgeous! What do you think of it.
101
+ USER: I think it's great. What are we going to do?
102
+ MH10: I think, perhaps, it is simplicity itself.
103
+ USER: Tell me then.
104
+ MH10: Your statement is, I want you?
105
+ USER: Ummm... no.
106
+ MH10: Yes, certainly.
107
+ USER: I think you're confused.
108
+ MH10: I think that you have been too busy to think of it.
109
+ USER: Are you saying what I think you're saying?
110
+ MH10: You know how to look into it.
111
+ USER: So do you by the sound of it.
112
+ MH10: I have been of material assistance to me for fifty minutes.
113
+ USER: Is this non-stop double entendre?
114
+ MH10: More than that it is a great thing for me to be able to understand that I have ever listened.
115
+ USER: Let's go and do something. Or get a bite to eat.
116
+ MH10: That is interesting.
117
+ USER: Why?
118
+ MH10: Because there are fifty who can reason analytically.
119
+ USER: Name them.
120
+ MH10: My name is Sherlock Holmes.
121
+ USER: And who are the other forty-nine?
122
+ MH10: I want to ascertain.
123
+ USER: You mean that you don't know?
124
+ MH10: Light a cigar and let me see if I can do what you have told me I think that both inferences are permissible.
125
+ USER: Well, it's been interesting. I must leave.
126
+ MH10: No crime, but a very interesting statement.
127
+ USER: Goodbye.
128
+ MH10: You must get home instantly and act.
129
+ MegaHAL Stopped
130
+ ```
131
+
132
+ USENET Announcement from 1995
133
+ -----------------------------
134
+
135
+ ```
136
+ Have a weird conversation with a computer
137
+ -----------------------------------------
138
+
139
+ Recently I added a feature to my home page allowing the user to "talk" to a
140
+ computer program of mine. The program was not written very intelligently, so
141
+ most of the time its replies consisted of incoherent babble.
142
+
143
+ Version 2 came and went, and now version 3 - MegaHAL - is ready for release.
144
+ The new features of the program are:-
145
+
146
+ * A second-order markov model is used, and it is doubly-linked. This
147
+ means that the computers sentences tend to be more grammatical.
148
+ * Punctuation is supported, which segments the sentences realistically.
149
+ * The reply given by the computer is based upon one of the words in
150
+ the input string. The word selected is one that maximises the ratio
151
+ (number of times since word was last used)/(number of times word has
152
+ appeared).
153
+ * The computers reply is generated from this keyword, and may be up to
154
+ 100 words long. The reply is generated in such a way that it is
155
+ impossible to get stuck in infinite loops.
156
+ * The keyword used is highlighted in the reply.
157
+
158
+ Some example conversations are given below:-
159
+
160
+ USER: you are crazy
161
+ HAL: WE *ARE* WEIRD.
162
+
163
+ USER: What is the meaning of life oh wise one?
164
+ HAL: *LIFE* IS THE ROOM.
165
+
166
+ USER: this is not getting us very far
167
+ HAL: YES I AM *GETTING* BORED OF GLOBAL DOMINATION.
168
+
169
+ USER: I have no idea.
170
+ HAL: *NO* IT IS IN HEAVEN.
171
+ USER: Is it really?
172
+ HAL: YES *IT* IS.
173
+
174
+ USER: My name is Jason.
175
+ HAL: I THINK ALAN TURING WAS A GIRLIE, JUST LIKE *MY* DEAR MAMA
176
+
177
+ The last response was generated from a combination of a discussion
178
+ about Alan Turing, and Monty Python's Lumberjack Song. Surely this
179
+ must be a sign of intelligence ;^)
180
+
181
+ Have fun!
182
+ ```
183
+
184
+ Copyright
185
+ ---------
186
+
187
+ Copyright (c) 2014 Jason Hutchens. See UNLICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "megahal"
18
+ gem.homepage = "http://github.com/jasonhutchens/megahal"
19
+ gem.license = "UNLICENSE"
20
+ gem.summary = %Q{MegaHAL is a learning chatterbot.}
21
+ gem.description = %Q{Have a weird conversation with a computer.}
22
+ gem.email = "jasonhutchens@gmail.com"
23
+ gem.authors = ["Jason Hutchens"]
24
+ gem.required_ruby_version = "~> 2.1"
25
+ # dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ desc "Code coverage detail"
36
+ task :simplecov do
37
+ ENV['COVERAGE'] = "true"
38
+ Rake::Task['spec'].execute
39
+ end
40
+
41
+ task :default => :spec
42
+
43
+ require 'yard'
44
+ YARD::Rake::YardocTask.new
data/UNLICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
data/bin/megahal ADDED
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'megahal'
4
+ require 'highline/import'
5
+ require 'hodel_3000_compliant_logger'
6
+ require 'ruby-progressbar'
7
+
8
+ say("<%= color('" + <<-EOS + "', BOLD) %>")
9
+ +----------------------------------------------------------+
10
+ | |
11
+ | # # ###### #### ## # # ## # |
12
+ | ## ## # # # # # # # # # # |
13
+ | # ## # ##### # # # ###### # # # |
14
+ | # # # # ### ###### # # ###### # |
15
+ | # # # # # # # # # # # # |
16
+ | # # ###### #### # # # # # # ###### |
17
+ | |
18
+ | Type "/help" for options and "/quit" to terminate. |
19
+ +----------------------------------------------------------+
20
+ EOS
21
+
22
+ class MegaLog
23
+ def initialize
24
+ @logger = Hodel3000CompliantLogger.new("megahal.log")
25
+ @logger.info "MegaHAL Started"
26
+ end
27
+ def exit
28
+ @logger.info "MegaHAL Stopped"
29
+ end
30
+ def error(message)
31
+ @logger.error "(#{message})"
32
+ message = HighLine.color("(#{message})", :dark, :red)
33
+ say(message)
34
+ end
35
+ def status(message)
36
+ @logger.info "(#{message})"
37
+ message = HighLine.color("(#{message})", :dark, :yellow)
38
+ say(message)
39
+ end
40
+ def user(message)
41
+ @logger.info "USER: #{message}"
42
+ end
43
+ def puts(message)
44
+ @logger.info "MH10: #{message}"
45
+ message = HighLine.color(message, :bold, :green)
46
+ say(message)
47
+ end
48
+ end
49
+
50
+ log = MegaLog.new
51
+
52
+ def list_menu(megahal, log)
53
+ choose do |menu|
54
+ menu.prompt = "? "
55
+ menu.choice(:cancel) do
56
+ log.status('cancelled')
57
+ return
58
+ end
59
+ menu.choice(:default) do
60
+ megahal.become(:default)
61
+ log.status('brain changed to the original loebner prize version')
62
+ end
63
+ menu.choice(:aliens) do
64
+ megahal.become(:aliens)
65
+ log.status('brain changed to bishop from aliens')
66
+ end
67
+ menu.choice(:ferris) do
68
+ megahal.become(:ferris)
69
+ log.status('brain changed to ferris bueller')
70
+ end
71
+ menu.choice(:pepys) do
72
+ megahal.become(:pepys)
73
+ log.status('brain changed to samuel pepys')
74
+ end
75
+ menu.choice(:pulp) do
76
+ megahal.become(:pulp)
77
+ log.status('brain changed to marsellus wallace from pulp fiction')
78
+ end
79
+ menu.choice(:sherlock) do
80
+ megahal.become(:sherlock)
81
+ log.status('brain changed to sherlock holmes')
82
+ end
83
+ menu.choice(:startrek) do
84
+ megahal.become(:startrek)
85
+ log.status('brain changed to data from star trek')
86
+ end
87
+ menu.choice(:starwars) do
88
+ megahal.become(:starwars)
89
+ log.status('brain changed to threepio from star wars')
90
+ end
91
+ end
92
+ log.puts(megahal.reply(nil))
93
+ end
94
+
95
+ def help_menu(megahal, log)
96
+ choose do |menu|
97
+ menu.prompt = "? "
98
+ menu.choice(:cancel) do
99
+ log.status('cancelled')
100
+ return
101
+ end
102
+ menu.choice(:reset) do
103
+ megahal.clear
104
+ log.status('brain cleared')
105
+ end
106
+ menu.choice(:brain) do
107
+ log.status('show brain menu')
108
+ list_menu(megahal, log)
109
+ end
110
+ menu.choice(:train) do
111
+ log.status('train on a text file')
112
+ filename = ask("Filename: ")
113
+ unless File.exists?(filename)
114
+ log.error("no such file")
115
+ else
116
+ begin
117
+ bar = ProgressBar.create(title: "Training", total: nil)
118
+ megahal.train(filename, bar)
119
+ bar.finish
120
+ log.status("training succesfull for '#{filename}'")
121
+ log.puts(megahal.reply(nil))
122
+ rescue => e
123
+ log.error(e.message)
124
+ end
125
+ end
126
+ end
127
+ menu.choice(:load) do
128
+ log.status('load a brain file')
129
+ filename = ask("Filename: ")
130
+ unless File.exists?(filename)
131
+ log.error("no such file")
132
+ else
133
+ begin
134
+ bar = ProgressBar.create(title: "Loading", total: nil)
135
+ megahal.load(filename, bar)
136
+ bar.finish
137
+ log.status("loading succesfull for '#{filename}'")
138
+ log.puts(megahal.reply(nil))
139
+ rescue => e
140
+ log.error(e.message)
141
+ end
142
+ end
143
+ end
144
+ menu.choice(:save) do
145
+ log.status('save a brain file')
146
+ filename = ask("Filename: ")
147
+ if File.exists?(filename)
148
+ log.error("file already exists")
149
+ else
150
+ begin
151
+ bar = ProgressBar.create(title: "Saving", total: nil)
152
+ megahal.save(filename, bar)
153
+ bar.finish
154
+ log.status("saving succesfull for '#{filename}'")
155
+ rescue => e
156
+ log.error(e.message)
157
+ end
158
+ end
159
+ end
160
+ if megahal.learning
161
+ menu.choice(:ignore) do
162
+ megahal.learning = false
163
+ log.status('stop learning from user input')
164
+ end
165
+ else
166
+ menu.choice(:learn) do
167
+ megahal.learning = true
168
+ log.status('start learning from user input')
169
+ end
170
+ end
171
+ menu.choice(:quit) do
172
+ log.status('exiting')
173
+ exit
174
+ end
175
+ end
176
+ end
177
+
178
+ megahal = MegaHAL.new
179
+ log.puts(megahal.reply(nil))
180
+ begin
181
+ loop do
182
+ input = ask("> ").strip
183
+ log.user(input)
184
+ if input.downcase =~ /^[\/\\#@](help|menu|info|list|h|\?)$/
185
+ log.status('show help menu')
186
+ help_menu(megahal, log)
187
+ elsif input.downcase =~ /^[\/\\#@](quit|exit|q|x)$/
188
+ log.status('exiting')
189
+ exit
190
+ else
191
+ log.puts(megahal.reply(input))
192
+ end
193
+ end
194
+ ensure
195
+ log.exit
196
+ end