Created
November 28, 2012 21:41
-
-
Save erochest/4164784 to your computer and use it in GitHub Desktop.
Jotto
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# http://www.ruby-doc.org/stdlib-1.9.3/libdoc/set/rdoc/Set.html | |
require 'set' | |
class Dictionary | |
attr_reader :words | |
def initialize | |
@words = Set.new [] | |
end | |
def load_file(dict_file) | |
IO.foreach dict_file do |line| | |
@words.add line.strip.downcase | |
end | |
self | |
end | |
def filter_length(n) | |
@words.keep_if { |word| word.length == n } | |
self | |
end | |
def filter_duplicate_letters | |
@words.keep_if do |word| | |
word_set = Set.new word.chars | |
word_set.length == word.length | |
end | |
self | |
end | |
def random_word | |
word_array = @words.to_a | |
word_array[Random.rand(word_array.length)] | |
end | |
end | |
class Secret | |
attr_accessor :word | |
def initialize | |
@word = nil | |
end | |
def is_word?(guess) | |
@word == guess | |
end | |
def get_matches(guess) | |
word_set = Set.new @word.chars | |
guess_set = Set.new guess.chars | |
(word_set & guess_set).length | |
end | |
def to_s | |
@word.to_s | |
end | |
def length | |
@word.length | |
end | |
end | |
class Jotto | |
attr_reader :dict | |
attr_reader :secret | |
attr_reader :guess_count | |
attr_reader :max_guess_count | |
def initialize(max_guess_count, dict) | |
@dict = dict | |
@secret = Secret.new | |
@guess_count = 0 | |
@max_guess_count = max_guess_count | |
end | |
def new_game | |
@secret.word = @dict.random_word | |
@guess_count = 0 | |
end | |
def guess(word) | |
word = word.downcase | |
@guess_count += 1 | |
{:guess => @guess_count, | |
:won => @secret.is_word?(word), | |
:over => (@secret.is_word?(word) || @guess_count >= @max_guess_count), | |
:matches => @secret.get_matches(word)} | |
end | |
end | |
class JottoConsole | |
def initialize(jotto) | |
@jotto = jotto | |
@wins = 0 | |
@losses = 0 | |
end | |
def loop | |
puts "Get ready to rumble!" | |
while true | |
game | |
break if ! again? | |
end | |
done | |
end | |
def again? | |
puts "Would you like to play again [Y/n]?" | |
gets[0].downcase != 'n' | |
end | |
def done | |
puts "You won #{@wins} games and lost #{@losses}." | |
if @wins > @losses | |
puts "Great job!" | |
elsif @wins == @losses | |
puts "We're evenly matched. Let's play again sometime." | |
else | |
puts "Better luck next time." | |
end | |
end | |
def game | |
@jotto.new_game | |
puts "I'm thinking of a #{@jotto.secret.length}-letter word. Guess what it is." | |
result = nil | |
while true | |
print "\nGuess #{@jotto.guess_count+1} of #{@jotto.max_guess_count}: " | |
word = gets.strip | |
if word == 'xyzzy' | |
puts @jotto.secret | |
puts result.inspect | |
else | |
result = @jotto.guess word | |
output_result result | |
break if result[:over] | |
end | |
end | |
output_game result[:won] | |
tally result[:won] | |
result[:won] | |
end | |
def output_result(result) | |
if result[:matches] == 1 | |
jot = 'jot' | |
else | |
jot = 'jots' | |
end | |
puts "#{result[:matches]} #{jot}" | |
end | |
def output_game(won) | |
if won | |
puts "That's it! Great guessing." | |
else | |
puts "You're out of turns! Better luck next time." | |
end | |
end | |
def tally(won) | |
if won | |
@wins += 1 | |
else | |
@losses += 1 | |
end | |
end | |
end | |
## Prepare the dictionary. | |
dictionary = Dictionary.new | |
dictionary.load_file("/usr/share/dict/words") | |
.filter_length(5) | |
.filter_duplicate_letters() | |
## Prepare the game. | |
jotto = Jotto.new(20, dictionary) | |
## Run it on the console. | |
JottoConsole.new(jotto).loop |
I also refactored it to make it slightly more decoupled. The Secret
class (SecretWord
in the specs) should know nothing about the Dictionary
class.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've simplified this for the specs here. For instance,
JottoConsole#loop
has been removed, so they no longer need to make it play multiple games, prompt to continue, or track wins and losses.