Ruby_Dice 0.0.1

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: b9b75ef4c4911ce1114a5f5fc95bd85dedeef93c
4
+ data.tar.gz: 208d27f1d71d807e2be51f966dd2260f41da09e5
5
+ SHA512:
6
+ metadata.gz: 5df78bac00770e5ec9b1b5d7e2bbc1bdf2a10e4bc35d337a2a4794f0c703c1e36cb26f12e2b93de7d12e2682afc9f9c7225adc432a4f5c3346f898878c87fdc6
7
+ data.tar.gz: 81850e8ce27a0f54bfbf74abdf3402832faaf4eaee53a0c3b08f253ba9dec64432d9ca3609d997641406bdd3aa05bdd7f2acc258c8ce97fb1f8a7174f2bd48a1
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Mario Martinez and Zachary Perlmutter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Ruby_Dice
2
+ A Yahtzee clone written in Ruby that runs in the terminal
3
+
4
+ This project is a personal project for educational purposes and becoming accustomed to the Ruby programming paradigm. Furthermore, it's to become accustomed to the Git workflow. Contributions are most welcome!
5
+ ## Badges
6
+ [![Inline docs](http://inch-ci.org/github/martimatix/Ruby-Dice.svg?branch=master)](http://inch-ci.org/github/martimatix/Ruby-Dice)
7
+ [![Code Climate](https://codeclimate.com/github/martimatix/Ruby-Dice/badges/gpa.svg)](https://codeclimate.com/github/martimatix/Ruby-Dice)
8
+ [![Build Status](https://travis-ci.org/martimatix/Ruby-Dice.svg)](https://travis-ci.org/martimatix/Ruby-Dice)
9
+ [![Test Coverage](https://codeclimate.com/github/martimatix/Ruby-Dice/badges/coverage.svg)](https://codeclimate.com/github/martimatix/Ruby-Dice)
10
+ ## Docs
11
+ * [rubydoc.info](http://www.rubydoc.info/github/martimatix/Ruby-Dice/master)
data/bin/Ruby_Dice ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ BEGIN {
3
+ if ARGV.include? "-h"
4
+ puts <<HELP
5
+ Usage: $ Ruby_Dice [--help]
6
+ \u00B7 --help -> Displays this help message
7
+
8
+ Gameplay:
9
+ Abbreviations:
10
+ \u00B7 1 -> Ones
11
+ \u00B7 2 -> Twos
12
+ \u00B7 3 -> Threes
13
+ \u00B7 4 -> Fours
14
+ \u00B7 5 -> Fives
15
+ \u00B7 6 -> Sixes
16
+ \u00B7 ss -> Small Straight
17
+ \u00B7 ls -> Large Straight
18
+ \u00B7 tok -> Three Of A Kind
19
+ \u00B7 fok -> Four Of A Kind
20
+ \u00B7 fh -> Full House
21
+ \u00B7 y -> Yahtzee
22
+ \u00B7 ? -> chance
23
+ HELP
24
+ exit
25
+ end
26
+ }
27
+
28
+ require "Ruby_Dice"
29
+
30
+ user = Player.new
31
+
32
+ until user.score.filled? do
33
+ puts ?\n
34
+ puts "New Turn".center(80)
35
+ puts ?\n
36
+ sleep 0.5
37
+ user.take_turn
38
+ end
data/lib/Ruby_Dice.rb ADDED
@@ -0,0 +1,4 @@
1
+ autoload :Dice, "dice"
2
+ autoload :Player, "player"
3
+ autoload :ScoreSheet, "scoresheet"
4
+ autoload :Scoring, "scoring"
data/lib/dice.rb ADDED
@@ -0,0 +1,59 @@
1
+ class Dice # Class for working with the 5 dice at the same time
2
+
3
+ attr_reader :values # @return [Array] the dice.
4
+
5
+ def initialize(values=Array.new(5) {new_dice}) # @param values [Array<Fixnum>] that contains 5 Fixnums
6
+ check_dice values
7
+ @values = values
8
+ end
9
+
10
+ # @!group Roll Methods
11
+
12
+ =begin
13
+ @raise [ArgumentError] if i element > 4
14
+ @return [void]
15
+ @param i [Array<Fixnum>] < 4
16
+ =end
17
+ def roll(dice_to_roll)
18
+ for index in dice_to_roll
19
+ raise ArgumentError, "Illegal index" if index > 4
20
+ @values[index] = new_dice
21
+ end
22
+ end
23
+
24
+ =begin
25
+ @note Rolls all the dice
26
+
27
+ @return [void]
28
+ =end
29
+ def roll_all; initialize; end
30
+
31
+ # @!endgroup
32
+
33
+ def to_s # @return [String] instance variable dice
34
+ print @values
35
+ end
36
+ alias display to_s
37
+
38
+ =begin
39
+ @param values [Array<Integer>]
40
+ @see (#check_dice)
41
+ @note setting dice for values for testing
42
+ @!parse attr_writer :values
43
+ =end
44
+ def values=(values)
45
+ check_dice values
46
+ @values = values
47
+ end
48
+
49
+ private
50
+ def new_dice; return (1..6).to_a.sample; end # @return [Fixnum] a random number between 1 and 6, inclusive
51
+ =begin
52
+ @param dice [Array<Fixnum>]
53
+ @return [void]
54
+ @raise [ArgumentError] if dice does not meet expectations
55
+ =end
56
+ def check_dice(dice)
57
+ raise(ArgumentError, "Array must have 5 Integers that are between 1 and 6") unless dice.length == 5 && dice.all? {|val| (val.is_a? Fixnum) && (val.between? 1,6)}
58
+ end
59
+ end
data/lib/player.rb ADDED
@@ -0,0 +1,90 @@
1
+ require_relative "scoresheet.rb"
2
+ require 'set'
3
+
4
+ class Player
5
+
6
+ ScoreAbbr = { # Abbreviations used in-game
7
+ ?1.to_sym => :ones,
8
+ ?2.to_sym => :twos,
9
+ ?3.to_sym => :threes,
10
+ ?4.to_sym => :fours,
11
+ ?5.to_sym => :fives,
12
+ ?6.to_sym => :sixes,
13
+ ss: :small_straight,
14
+ ls: :large_straight,
15
+ tok: :three_of_a_kind,
16
+ fok: :four_of_a_kind,
17
+ fh: :full_house,
18
+ y: :yahtzee,
19
+ "?".to_sym => :chance
20
+ }
21
+
22
+ attr_reader :score # @return [ScoreSheet]
23
+
24
+ def initialize
25
+ @score = ScoreSheet.new
26
+ end
27
+
28
+ =begin
29
+ @todo finish method
30
+ @return [void]
31
+ =end
32
+ def take_turn
33
+ turn_over = false
34
+ (1..3).each do |i|
35
+ display_dice i
36
+ turn_over = user_input i
37
+ break if turn_over
38
+ end
39
+ end
40
+
41
+ def display_dice(i)
42
+ sleep 0.5
43
+ puts String.new.center(80, ?-)
44
+ puts "Here are your dice. You have have #{3-i} #{i==2? "roll":"rolls"} remaining.\n\n"
45
+ puts "\tDice\t\tZ\tX\tC\tV\tB"
46
+ puts "\tValues\t\t" + score.dice.values.map{|value| value.to_s}.join(?\t)
47
+ puts String.new.center(80, ?-)
48
+ end
49
+
50
+ =begin
51
+ @param i [Fixnum] Amount of times rolled
52
+ @return [Boolean]
53
+ @note Gameplay
54
+ =end
55
+ def user_input(i)
56
+ if i < 3
57
+ print "Select dice to re-roll or select a score category: "
58
+ else
59
+ print "No rolls remaining. Select a score category: "
60
+ end
61
+ input = gets.chomp.downcase
62
+ input_symbol = input.to_sym
63
+ user_input = Set.new(input.split(''))
64
+ dice_controls = Set.new("zxcvb".split(''))
65
+
66
+ # If user wants to enter score
67
+ if ScoreAbbr.keys.include? input_symbol
68
+ score.enter_score ScoreAbbr[input_symbol]
69
+ puts score
70
+ @score.dice.roll_all
71
+ sleep 2
72
+ return true
73
+ # Else if user wants to roll the dice
74
+ elsif i < 3 && (user_input.subset? dice_controls)
75
+ dice_to_roll = (0..4).to_a.select { |index| input.include? dice_controls.to_a[index]}
76
+ @score.dice.roll(dice_to_roll)
77
+ sleep 0.5
78
+ 2.times {puts ?\n}
79
+ puts " Rolling Dice!\ ".center 80, "* "
80
+ sleep 1
81
+ return false
82
+ else
83
+ puts "Invalid input. Please try again."
84
+ sleep 1.5
85
+ puts ?\n
86
+ user_input i
87
+ end
88
+ end
89
+ end
90
+
data/lib/scoresheet.rb ADDED
@@ -0,0 +1,156 @@
1
+ require_relative "dice.rb"
2
+ require_relative "scoring.rb"
3
+
4
+ class ScoreSheet # Keeps score throughout the game
5
+ include Scoring
6
+
7
+ UpperScores = :ones, :twos, :threes, :fours, :fives, :sixes # The fields on the top section of the score sheet
8
+
9
+ LowerScores = :three_of_a_kind, :four_of_a_kind, :full_house, :small_straight, :large_straight, :chance, :yahtzee # The fields on the bottom section of the score sheet
10
+
11
+ attr_reader :sheet # @return [Hash] table of two element arrays where the first value is the score and the second is whether the field has been played
12
+ attr_reader :dice # @return [Dice]
13
+ attr_reader :num_yahtzees # @return [Fixnum] counter for number of yahtzees scored in the game
14
+ =begin
15
+ @param custom_dice [Array<Fixnum>] custom dice for testing
16
+ =end
17
+ def initialize(custom_dice=Array.new(5) {Dice.new.instance_eval "new_dice"})
18
+ @sheet, @dice, @num_yahtzees = Hash.new, Dice.new(custom_dice), 0
19
+ Array.new(UpperScores).concat(LowerScores).each {|s| @sheet[s] = [0, false]}
20
+ end
21
+
22
+ =begin
23
+ @param field [Symbol]
24
+ @return [void]
25
+ @raise ArgumentError
26
+ =end
27
+ def enter_score(field)
28
+ if field == :yahtzee && ((available :yahtzee) || @num_yahtzees > 0)
29
+ @sheet[field] = yahtzee, true
30
+ elsif available field
31
+ @sheet[field] = send(field, @dice.values), true
32
+ else
33
+ raise ArgumentError, "Score already entered."
34
+ end
35
+ end
36
+
37
+ =begin
38
+ @return [true] if the score sheet is completely filled and no legal moves remain
39
+ @return [false] if the score sheet is not completely filled and there are still legal moves to be made
40
+ =end
41
+ def filled?
42
+ @sheet.collect{|k,v| v[1]}.reduce{|r,e| r && e}
43
+ end
44
+
45
+ def raw_upper # @return [Fixnum]
46
+ @sheet.select{|x| UpperScores.include? x }.collect{|k,v| v[0]}.reduce :+
47
+ end
48
+
49
+ def upper_score_total # @return [Fixnum] the total score of the upper part of the ScoreSheet, including bonuses
50
+ raw_upper + upper_score_bonus
51
+ end
52
+ def lower_score_total # @return [Integer] The total score of the lower part of the ScoreSheet
53
+ @sheet.select{|x| LowerScores.include? x }.collect{|k,v| v[0]}.reduce :+
54
+ end
55
+ def total # @return [Integer] the grand total
56
+ lower_score_total + upper_score_total
57
+ end
58
+
59
+
60
+
61
+ =begin
62
+ Checks if upper score bonus can be awarded
63
+ @return [Fixnum] 0 if raw_upper < 63
64
+ @return [Fixnum] 35 if raw_upper >= 63
65
+ =end
66
+ def upper_score_bonus
67
+ if raw_upper >= 63 then 35
68
+ else
69
+ 0
70
+ end
71
+ end
72
+ =begin
73
+ @return [Fixnum]
74
+ Checks to see if you have all the of the same dice
75
+ =end
76
+ def yahtzee
77
+ if dice.values.all? {|x| x == dice.values[0]}
78
+ @num_yahtzees += 1
79
+ return sheet[:yahtzee][0] + 50 * 2 ** (@num_yahtzees - 1)
80
+ else
81
+ return 0
82
+ end
83
+ end
84
+
85
+ =begin
86
+ @todo Find a less complex way to create final string
87
+ @return [String]
88
+
89
+ =end
90
+ def to_s
91
+ ss = String.new
92
+ ss += %Q( S C O R E S H E E T ).center(80, ?=) + "\n\n"
93
+ (0..(UpperScores.length - 1)).each { |i| ss += print_score_sheet_line(i) }
94
+ ss += bonus_yahtzee_line
95
+ ss += "\n\n"
96
+ ss += "Total Score: #{total}".center(80) + ?\n
97
+ ss += (?= * 80) + ?\n
98
+ return ss
99
+ end
100
+
101
+
102
+ private # Helper methods
103
+
104
+ def available (field)
105
+ @sheet[field][1] == false
106
+ end
107
+
108
+ def score_sheet_line(left_val, right_val)
109
+ (left_val + "\t\t" + right_val).center(68) + ?\n
110
+ end
111
+ alias ssl score_sheet_line
112
+
113
+ def print_score_sheet_line(i);
114
+ upper, lower = format_score(UpperScores, i), format_score(LowerScores, i)
115
+ ssl upper, lower
116
+ end
117
+
118
+
119
+ def bonus_yahtzee_line
120
+ bonus_string, yahtzee_string = justify_score("Bonus", upper_score_bonus.to_s), format_score(LowerScores, LowerScores.length - 1)
121
+ ssl bonus_string, yahtzee_string
122
+ end
123
+ alias byl bonus_yahtzee_line
124
+ =begin
125
+ @return [String] the formatted string
126
+ @param index [Fixnum]
127
+ @param score_region [String, Array<String>]
128
+ Replace underscores with spaces
129
+ =end
130
+ def format_score(score_region, index)
131
+ score_label = "#{score_region[index]}".tr(?_, " ")
132
+ cap_label score_label
133
+ score_field = @sheet[score_region[index]]
134
+ return justify_score(score_label, "#{score_field[1]? score_field[0]:?-}")
135
+ end
136
+
137
+ def justify_score(label, score)
138
+ label.ljust(20) + score.rjust(3)
139
+ end
140
+
141
+ =begin
142
+ @param score_label [String]
143
+ @return [String]
144
+ Capitalize each letter of each word only if the score label has two words
145
+ Else only capitalize the first letter of the score label
146
+ =end
147
+ def cap_label(score_label)
148
+ if score_label.split.length == 2
149
+ score_label = score_label.split.map(&:capitalize)*' '
150
+ else
151
+ score_label.capitalize!
152
+ end
153
+ end
154
+ public
155
+ alias display to_s
156
+ end
data/lib/scoring.rb ADDED
@@ -0,0 +1,145 @@
1
+ require_relative "dice.rb"
2
+
3
+ module Scoring # methods for calculating score
4
+
5
+ UpperScores = :ones, :twos, :threes, :fours, :fives, :sixes # The fields on the top section of the score sheet
6
+
7
+ LowerScores = :three_of_a_kind, :four_of_a_kind, :full_house, :small_straight, :large_straight, :chance, :yahtzee # The fields on the bottom section of the score sheet
8
+
9
+ =begin
10
+ Checks to see if you have 3 of one kind of dice and 2 of another
11
+ @return [Fixnum] 25 if @dice.dice contains 3 of one Fixnum and 2 of another
12
+ @return [Fixnum] 0 if @dice.dice does not contain 3 of one Fixnum and 2 of another
13
+ =end
14
+ def full_house(dice)
15
+ f_table = freq dice
16
+ if (f_table.length == 2 && f_table.has_value?(3)) || f_table.length == 1 then return 25
17
+ else; return 0
18
+ end
19
+ end
20
+
21
+ =begin
22
+ Checks to see if you have 4 of the same dice
23
+ @return [Fixnum] 0 if <= 4 indices have the same value
24
+ @return [Fixnum] dice.reduce(:+) if >= 4 indices have the same value
25
+ @see (#three_of_a_kind)
26
+ =end
27
+ def four_of_a_kind(dice)
28
+ of_a_kind dice, 4
29
+ end
30
+
31
+ =begin
32
+ @param d [Array<Fixnum>] the dice to be tested
33
+ @return [Fixnum] the score
34
+ =end
35
+ def ones(d)
36
+ single_face d, 1
37
+ end
38
+
39
+ def twos(d) # @see (#ones)
40
+ single_face d, 2
41
+ end
42
+
43
+ def threes(d) # @see (#ones)
44
+ single_face d, 3
45
+ end
46
+
47
+ def fours(d) # @see (#ones)
48
+ single_face d, 4
49
+ end
50
+
51
+ def fives(d) # @see (#ones)
52
+ single_face d, 5
53
+ end
54
+
55
+ def sixes(d) # @see (#ones)
56
+ single_face d, 6
57
+ end
58
+
59
+ =begin
60
+ @see (#four_of_a_kind)
61
+ Checks to see if you have 3 of the same dice
62
+ @return [Fixnum] dice.reduce(:+) if there is <= 3 of the same value
63
+ @return [Fixnum] 0 if you do not have a three of a kind
64
+ =end
65
+ def three_of_a_kind(dice)
66
+ of_a_kind dice, 3
67
+ end
68
+
69
+ =begin
70
+ @param dice [Array<Fixnum>] the dice to be evaluated
71
+ @return [Fixnum] the sum of all the dice
72
+ =end
73
+ def chance(dice)
74
+ dice.reduce :+
75
+ end
76
+
77
+ =begin
78
+ @param dice [Array<Fixnum>] the dice to be tested
79
+ @return [Fixnum] 30 if there are 3 consecutive Fixnums in dice
80
+ @return [Fixnum] 0 if there are not three conscecutive Fixnums in dice
81
+ @see (#large_straight)
82
+ =end
83
+ def small_straight(dice)
84
+ straight dice, 4, 30
85
+ end
86
+ alias SS small_straight
87
+
88
+ =begin
89
+ @param dice [Array<Fixnum>] the dice to be tested
90
+ @return [Fixnum] 40 if there are 4 consecutive Fixnums in dice
91
+ @return [Fixnum] 0 if there are not three conscecutive Fixnums in dice
92
+ @see (#small_straight)
93
+ =end
94
+ def large_straight(dice)
95
+ straight dice, 5, 40
96
+ end
97
+ alias LS large_straight
98
+
99
+ private # Helper methods for score calculation
100
+
101
+ def single_face(dice, value)
102
+ v = dice.select{|number| number == value}.reduce :+
103
+ unless v.nil?
104
+ return v
105
+ else
106
+ return 0
107
+ end
108
+ end
109
+
110
+ def freq(dice)
111
+ dice.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
112
+ end
113
+
114
+ def modal_frequency(dice)
115
+ freq(dice).max_by{|k,v| v}[1]
116
+ end
117
+
118
+ def of_a_kind(dice, limit)
119
+ if modal_frequency(dice) >= limit
120
+ dice.reduce :+
121
+ else
122
+ 0
123
+ end
124
+ end
125
+
126
+ =begin
127
+ @param dice [Fixnum] the dice to be tested
128
+ @param limit [Fixnum] = 4 for small straight
129
+ @param limit [Fixnum] = 5 for large straight
130
+ common code for both small straight (SS) and large straight (LS)
131
+ @param score [Fixnum] is the score to return
132
+ @return [Fixnum] score
133
+ =end
134
+ def straight(dice, limit, score)
135
+ #each_cons is generating every possible value for a straight of length limit
136
+ (1..6).each_cons(limit).each do |i|
137
+ # Asking if i is a subset of dice
138
+ if (i - dice).empty?
139
+ return score if (i - dice)
140
+ end
141
+ end
142
+ return 0
143
+ end
144
+
145
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Ruby_Dice
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Zachary Perlmutter
8
+ - Mario Martinez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-12-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '3.1'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '3.1'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec-its
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ description: |
43
+ This project is a personal project for educational purposes and becoming accustomed to the Ruby programming paradigm.
44
+ Furthermore, it's to become accustomed to the Git workflow.
45
+ Contributions are most welcome!
46
+ email:
47
+ - zrp200@gmail.com
48
+ - zenitram.oiram@gmail.com
49
+ executables:
50
+ - Ruby_Dice
51
+ extensions: []
52
+ extra_rdoc_files:
53
+ - LICENSE.md
54
+ - README.md
55
+ files:
56
+ - LICENSE.md
57
+ - README.md
58
+ - bin/Ruby_Dice
59
+ - lib/Ruby_Dice.rb
60
+ - lib/dice.rb
61
+ - lib/player.rb
62
+ - lib/scoresheet.rb
63
+ - lib/scoring.rb
64
+ homepage:
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements:
83
+ - terminal
84
+ rubyforge_project:
85
+ rubygems_version: 2.4.5
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: A Yahtzee clone written in Ruby that runs in the terminal
89
+ test_files: []