# Markov Chain (#74)

**URL:** <https://www.ruby-forum.com/t/markov-chain-74/93794>\
**Category:** Ruby\
**Created:** [April 10, 2006, 3:44pm UTC](https://www.ruby-forum.com/t/markov-chain-74/93794 "2006-04-10T15:44:29Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [April 10, 2006, 3:44pm UTC](https://www.ruby-forum.com/t/markov-chain-74/93794/1 "2006-04-10T15:44:29Z")

</div>

# Author: Shane E.

# 

# Quiz 74: Markov Chains

# 

# This is based off of the classic Mark V. Shaney program. The

# basic algorithm only looked at the previous two words. I have

# changed it, so that it can check the previous four, previous three,

# previous two, or the previous word(s) to find the next reasonable

# choice. I also added some smarter end of phrase checks along with

# the standard rubifying.

# 

# I was thinking, but have not tried, what would happen if you

# sent source code through the algorithm? Obviously some things

# would need to be changed for “phrase\_breaks”, but I wonder if

# anything would actually sucessfully run.

class MarkovChain

```
attr_reader :phrase_breaks, :phrases

def initialize( book )
    @book = book
    @phrases = Array.new( 4 ).fill( Hash.new )
    @phrase_breaks = Array.new
end

def read( book = @book )
    prev = ['', '', '', '']
    words = File.open( book ).read.split
    words.each do |word|
        word.gsub!( /"/, '' )
        unless prev[0].eql?( '' )
            @phrases.each_index do |i|
                prev_words = prev[0 .. i].join( ' ' )
                @phrases[i][prev_words] = Array.new unless
                    @phrases[i].has_key?( prev_words )
                @phrases[i][prev_words] << word.downcase
                @phrase_breaks << prev_words if prev_words.match(

```

/[.!?]$/ )  
end  
end  
prev.pop and prev.insert( 0, word )  
end  
end

```
def get_chain( num_want = 5 )
    chain = Array.new
    num_made = 0
    prev = ['', '', '', '']
    prevs = Array.new( 4 )
    prev.each_index { |i| prevs[i] = prev[0 .. i].join( ' ' ) }
    while num_made < num_want do
        until @phrases[3].has_key?( prevs[3] ) or
              @phrases[2].has_key?( prevs[2] ) or
              @phrases[1].has_key?( prevs[1] ) or
              @phrases[0].has_key?( prevs[0] )
            prev = @phrase_breaks[ rand( @phrase_breaks.length )

```

].split  
prev.each\_index { |i| prevs[i] = prev[0 … i].join(  
’  
’ ) }  
end  
words = Array.new  
@phrases.each\_index do |i|  
prev\_words = prev[0 … i].join( ’ ’ )  
words = @phrases[i][prev\_words] if  
@phrases[i].has\_key?( prev\_words )  
end  
word = words[rand( words.length )]  
chain \<\< word  
prev.shift and prev.push( word )  
prev.each\_index { |i| prevs[i] = prev[0 … i].join( ’ ’  
)  
}  
num\_made += 1 if prev[-1].match( /[.!?]$/ )  
end  
chain  
end

end

mChain = MarkovChain.new( ARGV[0] )  
mChain.read  
print mChain.get\_chain.join( ’ ’ ), “\n”
