1
\$\begingroup\$

This was an assignment from "The Odin Project". This is my first program using JSON. I've been told I need work on structuring my classes, any advice will be appreciated!

https://github.com/jmooree30/Hangman/upload/master

require 'json'
load 'display.rb'

class Hangman
  attr_accessor :name
  @name = name

  def initialize
    puts "What is your name?"
    @name = gets.chomp
    puts "
################################################
                   HANGMAN
################################################
               _________
              |        
              |       |
              |       O
              |      /|\\
              |       |
              |      / \\
              |
              -----------------
Welcome #{@name} to Hangman. The computer will generate
a 5-12 letter random word. You will try to guess
that word one letter at a time. Try to solve the
puzzle before time runs out!
\n 
"

  end
end

class Gameplay
  attr_accessor :array, :filestuff, :random_word, :cipher, :random_word2, :counter
  def initialize
  @array = []
  @filestuff = File.foreach('5text.txt') do |x|
      chomped = x.chomp
      @array << chomped if (chomped.length >= 5 and chomped.length <= 12)
    end
  @random_word = @array.sample
  @cipher = @random_word.gsub(/[a-z]/, '*').split(//)
  @random_word2 = @random_word.split(//)
  @counter = 5
  puts "Would you like to load a saved game? Y/N"
  saved_game = gets.chomp
  if saved_game == "Y"
    game_file = File.read("saved.json")
    data = JSON.parse(game_file)
    @filestuff = data["filestuff"]
    @random_word = data["random_word"]
    @cipher = data["cipher"]
    @random_word2 = data["random_word2"]
    @counter = data["counter"]
  end
  puts @cipher.join
  puts "Enter a letter."

  def to_json
    JSON.generate({filestuff: @filestuff, random_word: @random_word, cipher: @cipher, random_word2: @random_word2})
  end

  def save_game(string) 
    File.open("saved.json", "w") do |game_file|
  game_file.write(string)
    end
  end

  def choice(n)
    @random_word2.each_with_index do |i,index|
      if i == n
        @cipher[index] = i 
      end 
    end 
      if n == @random_word2.join.to_s
        puts "You win"
        puts "would you like to start another game? Y/N"
        new_game = gets.chomp
        if new_game == "Y"
          Hangman.new
          else exit 
        end
      end
      if @random_word2.include?(n) == false
        @counter -= 1
        display
        puts "#{@counter} guesses remaining."
      end
      if @counter == 0
        puts "would you like to start another game? Y/N"
        new_game = gets.chomp
        if new_game == "Y"
          else exit 
        end
      end 
      if n == "1"
          save_game(to_json)
        end 
        puts @cipher.join
        puts "Want to save? Press 1 to save."    
  end

  @counter = 5
  while @counter > 0 
    choice(gets.chomp)
  end

 end 
end
Hangman.new
Gameplay.new
\$\endgroup\$
  • \$\begingroup\$ Do you have a link to the assignment? Are those classes & names required? \$\endgroup\$ – Mark Thomas Aug 25 '17 at 21:38
  • \$\begingroup\$ theodinproject.com/courses/ruby-programming/lessons/… <<link to assignment. I just realized I never implemented it to be case sensitive I will fix that later. The names and classes are not required. I feel my biggest weakness right now is how I go about structuring everything. \$\endgroup\$ – Jacob Moore Aug 25 '17 at 22:17
0
\$\begingroup\$

One of the ways to structure your objects is to describe your problem in a sentence or two, then turn the nouns into classes, and the verb phrases into methods.

In this case, you may end up with Hangman (the game), Player, and not really much else. You can choose to put the user interaction in Hangman or you can create a separate class for it, as you did with GamePlay.

There are a couple of things to keep in mind:

  • Make sure the interface makes sense. For example Hangman.new; GamePlay.new to start a game is a bit awkward. Whenever the interface is awkward it usually means there's a problem with the design. In this case, you have to manage two classes independently and you may have noticed that there's not much more you can do with your Hangman class as it is without managing it with logic external to either class.
  • Apply the right behavior to the right object. Assuming Hangman represents the game, then it should do all the game stuff: new (begin), save, load, guess, show_word, etc. Then GamePlay would just be user interaction, calling the Hangman game's methods as needed.

So let's think about the interface a bit: instead of

Hangman.new
Gameplay.new

perhaps you want something more like GamePlay.begin, or if you want to specify what game it is, maybe Hangman::GamePlay.begin which means you'd put the classes in an organizing module.

This exercise structures everything around a mental model. Then it's up to you to keep the model clean. Every time you need to add functionality, ask yourself: should this go into Hangman or GamePlay? Make sure that you have the separation of concerns clear.

In your case you have user prompts in two classes which is not a clean separation of concerns and may cause problems (e.g. GamePlay doesn't know the player's name).

Hopefully this makes sense. It tackles just the organizing step, let me know if you want assistance refactoring to a different model.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I'm currently waist deep in the next section trying to write a recursive merge sort. If I ever figure it out I'll get back to you on refactoring a new model. \$\endgroup\$ – Jacob Moore Aug 27 '17 at 3:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.