GithubHelp home page GithubHelp logo

risk_calculator's People

Contributors

abacha avatar practicingruby avatar semmons99 avatar

Watchers

 avatar  avatar

risk_calculator's Issues

lib/risk_calculator.rb should exist

A file called lib/risk_calculator.rb should exist and require all other necessary files in your library. This makes it possible to easily do require "risk_calculator" and get all of your code loaded at once. In your bin file, you'll probably do: require_relative "../lib/risk_calculator"

Issues with Dijkstra

  1. This should be in a namespace, i.e. the file should be in lib/risk_calculator/dijkstra.rb, and the class name should be wrapped in a module like:
module RiskCalculator
  class Dijkstra
  end
end

The details about this are in the s10-notes about code structure

  1. Your initialize method is listed under your private methods. However, Ruby will automatically make initialize private no matter where you put it in your file. Since it is something that all your instance methods depend on, the standard place to put it is directly above the rest of your instance method definitions (i.e. right above def add_path in this case). Placing it down under the private keyword makes it easy to miss for the code reader.

Gemfile doesn't have sources

Please add source :rubygems to your Gemfile.

Plus on a side note, in ruby 1.9 it's no longer required to require 'rubygems' and you appear to use ruby 1.9 =)

README is out of date

The README does not reflect how the code currently operates, and so I cannot easily test to make sure it works as expected. Please update.

Assets directory should be renamed data

It's common outside of Rails to refer to your directory which contains supporting data as the data directory, not the assets directory. This is so common in fact that it's referenced in a draft proposal for a Ruby Packaging Standard. Please re-name the assets folder to data/

Initialize the instance variables

To me the following in the dijkstra method seems rather hacky:

(@target ||= {})[node] = node == origin ? 0 : INFINITY
(@prev ||= {})[node]   = -1

I'm especially talking about (@target ||= {}). If I don't missunderstand the code you could just do:

@target = {}
@prev   = {}

before the loop which would be a lot more readable.

attr_reader/attr_accessor usage

In Territory:

attr_reader   :name
attr_reader   :borders

can be written as:

attr_reader :name, :borders

Plus if you define army yourself you should rather user attr_writer than attr_accessor

The code _lib/risk_calculator.rb_ should be moved int _lib/risk_calculator/application.rb_ and made modular

Currently the code in lib/risk_calculator.rb is just a script. It would be better off to make it modular, using the technique outlined in this Practicing Ruby article. In particular, see the code samples with reference RCat::Application as well as the section on building an executable script.

You can also look at any of the s10-int projects for ideas on how the students built their executables for those.

Board.read_from_file might be better as Board.from_file(...)

Your Board.read_from_file method looks correct, but the name doesn't sound great to me. Consider renaming it to Board.from_file, simply because this seems to read more naturally. Compare the two examples in context and read them aloud in your head and see which expresses the idea better:

board = Board.read_from_file(filename)

#vs.

board = Board.from_file(filename)

To me the former emphasizes the action and the latter emphasizes the result. Emphasizing the result is better than emphasizing the action when you are returning a value as opposed to simply causing a side effect.

Issues with bin/risk_calculator.rb

  1. Rather than putting most of your code in your script, you should create a RiskCalculator::Application class in lib/risk_calculator/application.rb which encapsulates your logic. While the advice in this Practicing Ruby article is probably more than what you need, you can get the idea of the basic structure from skimming through it and looking at how bin/rcat interacts with RCat::Application

  2. Rather than using require "highline/import", I'd recommend using require "highline" and then doing something lik:

io = HighLine.new
io.menu do
  #...
end

This will prevent your application from causing HighLine to pollute the global namespace.

  1. Rather than calling this file bin/risk_calculator.rb it should be called bin/risk_calculator and use a shebang line to indicate it's a Ruby executable. This makes it possible to run the command in your shell as risk_calculator instead of risk_calculator.rb, which will make it feel more like an ordinary UNIX command.

  2. Instead of inheriting from Exception, it's better to inherit from StandardError. This is because StandardError is the class Ruby provides for use as a parent class for custom exceptions, is what a bare rescue matches. The kinds of exceptions which inherit from Exception typically are things that can't be rescued in a general case, such as syntax errors.

  3. While it's optional, I'd encourage you to write InvalidParameterException = Class.new(StandardError) instead of defining a bare class definition, simply because it's a bit more aesthetically pleasing and is functionally equivalent.

  4. To get the full benefits of HighLine, it's best to use it for all your console IO needs. That means replacing calls to puts with calls to HighLine#say.

  5. Instead of params = _params.split(';'), consider using a more intuitive name for _params, such as raw_params or params_text, yielding something like params = params_text.split(';')

  6. Please remove any unused code rather than leaving it commented out in the source.

Too long methods

In general try to keep your methods ~10 lines of code max (usually the smaller the bestter). Smaller methods are way easier to read and to follow. You can usually get the lines of a method down by extracting some sub methods that do one unit of work together. This also helps the reader a lot since descriptive method names act as little comments.

I'm particualarly talking about:

  • Board#conquer
  • Graph#dijkstra

For the dijkstra method, it would be good imo if you could get rid of while - imo there are more rubyish alternatives to that there. Also I'm not a big fan of break try if you can get it out keeping the code intact and readable.

Variable naming

t isn't a very descriptive variable name, from the context it is mostly clearly what you mean, however territory would usually be better.
And for instance:

 borders.each do |t|

threw me off. I believe borders should rather be named bordering_territories or something like that. Also there we got the t (which stands for territory) and another territory variable in the same method (Board#add_borders). Try to show the difference between the two in the naming.
Remember that code is read many more times than written. :-)

Files should be in a namespaced directory

For any Ruby library, it is best to introduce only a single name into the global code loading space. To do so, you should have a structure like:

lib/risk_calculator.rb
lib/risk_calculator/board.rb
lib/risk_calculator/dijkstra.rb
lib/risk_calculator/territory.rb

The lib/risk_calculator.rb file should consist of only require calls to load external dependencies, and require_relative calls to load all your project files. These conventions are very standard and are included in the Ruby Packaging Standard draft proposal.

Currently lib/risk_calculator.rb is serving as an executable script which isn't standard, I'll file another ticket about how to fix that.

Issues with Territory

  1. This should be in a namespace, i.e. the file should be in lib/risk_calculator/territory.rb, and the class name should be wrapped in a module like:
module RiskCalculator
  class Territory
  end
end

The details about this are in the s10-notes about code structure

  1. The army method should probably be armies or army_strength or army_size, or something along those lines.

  2. It's not clear to me why army returns an absolute value. Is this number ever negative, and if so, why?

Issues with Board

  1. This should be in a namespace, i.e. the file should be in lib/risk_calculator/board.rb, and the class name should be wrapped in a module like:
module RiskCalculator
  class Board
  end
end

The details about this are in the s10-notes about code structure

  1. Board#read_board_file should be lifted up into Board.from_file

I provided an outline of how to do this in this gist. The benefit of moving your file processing logic to a class level factory method is that it makes it so that your Board object is not so tightly coupled to a single file-based representation. This will make testing as well as using board objects in a different context much, much easier.

Improved file handling

In board.rb your initialize method has quite a bit of file handling. while loops aren't really the ruby way, after reading the first line (which is different I suppose) then you can simply do:

  f.each_line (...)

You should also think about naming f file. Plus I'd rather see this file handling in its own method (it's too low level for the rest of the initialize method)

Be clear on what values mean

In the Dijkstra algorithm

(@prev ||= {})[node]   = -1

From what I know about Dijkstra I guess that this means that there is no previous node yet. If I'm correct then I guess it would be better to use a more obvious value, like nil or define a constant that communicates this behaviour better.

Use Hash with custom value for non existent keys

In the dijkstra I see a lot of this:

(@graph[origin] ||= {})[target] = weight

Which is basically that you want the default value of {} if a key isn't present, right? Try doing this:

@graph = Hash.new({})

This way you can set any default value that is returned when the key doesn't exist. Should make the code a lot more readable =)

Infinity

We actually have infinity in ruby, target version imo was 1.9.2 it works on 1.9.3 for me:

Float::INFINITY

For use in your Dijkstra algorithm =)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.