functional-yahtzee 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ee9515ae7d6e62b2c338eaa390b462f0a00fd47f
4
+ data.tar.gz: 41387d21afb1951765a3d9c8cf5aee97ada9ec8a
5
+ SHA512:
6
+ metadata.gz: 763b40ce6d6b310e281eb5b8ad5c56f50827c06048576d6313f2c3ffb836c480c5c7fa869cdb74e76b3450c1e52222c2851330ae0217c8cd9db296c8d849074b
7
+ data.tar.gz: 806e1457646e3748eb377893e0f34b55c38fecc7fa9b351aa8905e4c31efee611124ad191efa19533231116de11eecc89aa6bd62e9254a1e9ad592058f32ead2
@@ -0,0 +1 @@
1
+ --- {}
@@ -0,0 +1,2 @@
1
+ coverage
2
+ *.gem
@@ -0,0 +1 @@
1
+ 2.1.0-dev
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ notifications:
6
+ email: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,71 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ functional-yahtzee (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ celluloid (0.15.2)
10
+ timers (~> 1.1.0)
11
+ coderay (1.1.0)
12
+ coveralls (0.7.0)
13
+ multi_json (~> 1.3)
14
+ rest-client
15
+ simplecov (>= 0.7)
16
+ term-ansicolor
17
+ thor
18
+ docile (1.1.0)
19
+ ffi (1.9.3)
20
+ formatador (0.2.4)
21
+ guard (2.2.3)
22
+ formatador (>= 0.2.4)
23
+ listen (~> 2.1)
24
+ lumberjack (~> 1.0)
25
+ pry (>= 0.9.12)
26
+ thor (>= 0.18.1)
27
+ guard-minitest (2.0.0)
28
+ guard (~> 2.0)
29
+ minitest (>= 3.0)
30
+ listen (2.2.0)
31
+ celluloid (>= 0.15.2)
32
+ rb-fsevent (>= 0.9.3)
33
+ rb-inotify (>= 0.9)
34
+ lockfile (2.1.0)
35
+ lumberjack (1.0.4)
36
+ method_source (0.8.2)
37
+ mime-types (2.0)
38
+ minitest (5.0.8)
39
+ multi_json (1.8.2)
40
+ pry (0.9.12.3)
41
+ coderay (~> 1.0)
42
+ method_source (~> 0.8)
43
+ slop (~> 3.4)
44
+ rake (10.1.0)
45
+ rb-fsevent (0.9.3)
46
+ rb-inotify (0.9.2)
47
+ ffi (>= 0.5.0)
48
+ rest-client (1.6.7)
49
+ mime-types (>= 1.16)
50
+ simplecov (0.8.1)
51
+ docile (~> 1.1.0)
52
+ lockfile (>= 2.1.0)
53
+ multi_json
54
+ simplecov-html (~> 0.8.0)
55
+ simplecov-html (0.8.0)
56
+ slop (3.4.7)
57
+ term-ansicolor (1.2.2)
58
+ tins (~> 0.8)
59
+ thor (0.18.1)
60
+ timers (1.1.0)
61
+ tins (0.13.1)
62
+
63
+ PLATFORMS
64
+ ruby
65
+
66
+ DEPENDENCIES
67
+ bundler (~> 1.3)
68
+ coveralls (~> 0.7)
69
+ functional-yahtzee!
70
+ guard-minitest (~> 2.0)
71
+ rake (~> 10.1)
@@ -0,0 +1,6 @@
1
+ # Units
2
+ guard :minitest do
3
+ watch(%r{^test/(.*)\/?(.*)_test\.rb})
4
+ watch(%r{^lib/(.*/)?([^/]+)\.rb}) { |m| "test/unit/#{m[1]}#{m[2]}_test.rb" }
5
+ watch(%r{^test/test_helper\.rb}) { 'test' }
6
+ end
@@ -0,0 +1,115 @@
1
+ # Functional Yahztee in RUBY
2
+
3
+ The goal of this project is to build the dice game Yahtzee in a functional, concise, testable manner. The code within is
4
+ highly opinionated, and is ahallmark of my style. This is my perfect world where code and beauty collide.
5
+
6
+ If you are like me, and I know I am... you have something to learn from this code here. This is how you write clean, decoupled, testable, functional ruby code.
7
+
8
+ Expect a series of screencasts discussing the choices I have made here to get the results I ended up with.
9
+
10
+ [rubyquiz #19](http://rubyquiz.com/quiz19.html) proposed this
11
+ problem, and a very imperative solution was provided by James
12
+ Edward Gray II, one my ruby heros. I doubt he would write this the same way could he revisit the problem.
13
+
14
+ This is my contribution to the same problem, with the additional task of being as functional as possible and downright pretty code.
15
+
16
+ ## Ruby is dead
17
+
18
+ 1.8 was the Ruby of your Father. It was for a world with single cores and CPUs growing at a predictable rate each year. Ruby 1.8 is dead....
19
+
20
+ ## Long Live Ruby
21
+
22
+ Thankfully the teams behind Rubinius, JRuby, and even MRI have been
23
+ working on this problem. I am happy to say that Ruby 2.0+ is
24
+ perfectly suited as a stepping stone to get you into the new
25
+ world, a world where CPU matters not, and cores own.
26
+
27
+ ## Requirements
28
+
29
+ This is being developed in Ruby 2.1 with CI running on 2.0 and 1.9.3. My style tends to stay away from external dependencies whenever I can. (read: I am not afraid to code what I need)
30
+
31
+ I haven't gemmified this as of yet, so a clone and bundle with ruby
32
+ 1.9.3+ and you can run the tests. I would love to get this running on rubinius as well.
33
+
34
+ ```bash
35
+ cd <cloned_path>
36
+ rake
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ```ruby
42
+ score_card = ScoreCard.new
43
+ writer = ScoreCard.persist(score_card)
44
+
45
+ Round.new do
46
+ roll1 = first_roll # => [1,2,3,5,5]
47
+ roll2 = second_roll([1,2,3,5]) # => [1,2,3,5,3]
48
+ roll3 = third_roll([1,2,3,5]) # => [1,2,3,5,4]
49
+
50
+ score_card = Scoring.score(roll3, :large_straight, &writer)
51
+ end
52
+
53
+ Round.new do
54
+ roll1 = first_roll # => [1,1,3,5,5]
55
+ roll2 = second_roll([1,1,5,5]) # => [1,1,5,5,1]
56
+
57
+ score_card = Scoring.score(roll2, :full_house, &writer)
58
+ end
59
+
60
+ # ...
61
+
62
+ upper_scores = score_card.to_hash.select do |k,_|
63
+ ScoreCard.upper_keys.include? k
64
+ end
65
+ score_card = Scoring.score_upper_total(upper_scores, &writer)
66
+
67
+ lower_scores = score_card.to_hash.select do |k,_|
68
+ ScoreCard.lower_keys.include? k
69
+ end
70
+
71
+ upper_total = score_card.upper_total
72
+ game_scores = lower_scores.merge(upper_total: upper_total)
73
+ score_card = Scoring.score_game_total(game_scores, &writer)
74
+
75
+ puts "Grand total: #{score_card.game_total}"
76
+
77
+ ```
78
+
79
+ ## Contributions
80
+
81
+ I am not accepting contributions at this time, this is my showcase code. This is my own highly opinionated software, inspired by all those listed in the section below *Many Thanks*.
82
+
83
+ ## Many Thanks
84
+
85
+ I would like to say thank you to all the people I have sucked off of like a vampiric zombie since 2007. Some of these were from videos of talks, some were pairing sessions, some were just friends on irc who liked to bang Ruby.
86
+
87
+ * James Edward Gray II
88
+ * Josh Susser
89
+ * Ben Curtis
90
+ * Avdi Grimm
91
+ * Zed
92
+ * Gregg Pollack
93
+ * Daniel Fischer
94
+ * Jose Valim
95
+ * Aaron Patterson
96
+ * Corey Haines
97
+ * Ryan Bates
98
+ * Steve Klabnik
99
+ * Brian Guthrie
100
+ * Fabio Akita
101
+ * Rick Olsen
102
+ * Obie Fernandez
103
+ * Sandi Metz
104
+ * Phil Cohen
105
+ * Jessica Kerr
106
+
107
+ Whew.. Sorry if I missed you!
108
+
109
+ ## Health
110
+
111
+ * My code can beat your code up
112
+
113
+ [![Build
114
+ Status](https://travis-ci.org/dreamr/functional_yahtzee.png)](https://travis-ci.org/dreamr/functional_yahtzee)
115
+ [![Coverage Status](https://coveralls.io/repos/dreamr/functional_yahtzee/badge.png?branch=master)](https://coveralls.io/r/dreamr/functional_yahtzee?branch=master) [![CodeClimate Grade](https://codeclimate.com/github/dreamr/functional_yahtzee.png)](https://codeclimate.com/github/dreamr/functional_yahtzee/code)
@@ -0,0 +1,13 @@
1
+ require 'rake/testtask'
2
+
3
+ desc "Open a console with the basic libs loaded"
4
+ task :console do
5
+ sh "irb -rubygems -Ilib -r game.rb"
6
+ end
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.libs = ["lib", "test"]
11
+ end
12
+
13
+ task :default => :test
Binary file
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ lib = File.expand_path("../../lib",__FILE__)
4
+ $:.unshift lib
5
+
6
+ module Yahtzee
7
+ require 'game'
8
+
9
+ module_function
10
+
11
+ def repl
12
+ -> prompt do
13
+ print prompt
14
+ handle_input(gets.chomp!)
15
+ end
16
+ end
17
+
18
+ def handle_input(input)
19
+ begin
20
+ result = eval(input)
21
+ rescue Exception => e
22
+ result = e.message
23
+ end
24
+ puts result.to_s
25
+ end
26
+ end
27
+
28
+ loop do
29
+ Yahtzee.repl[">> "]
30
+ end
31
+
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'yahtzee'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "functional-yahtzee"
8
+ spec.version = Yahtzee::VERSION
9
+ spec.authors = ["Dreamr"]
10
+ spec.email = ["dreamr.okelly@gmail.com"]
11
+ spec.description = %q{Functional yahtzee}
12
+ spec.summary = %q{Yahztee in functional ruby}
13
+ spec.homepage = "http://github.com/dreamr/functional_yahtzee"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake", "~> 10.1"
23
+ spec.add_development_dependency "guard-minitest", "~> 2.0"
24
+ spec.add_development_dependency "coveralls", "~> 0.7"
25
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'yahtzee'
2
+
3
+ # Yahtzee::Dice is just a dice dsl for yahtzee
4
+ module Yahtzee::Dice
5
+ DIE_FACES = 6
6
+ MAX_DIE_COUNT = 5
7
+
8
+ def self.roll(num)
9
+ limit = num > MAX_DIE_COUNT ? MAX_DIE_COUNT : num
10
+ 1.upto(limit).map {|die| rand(1..DIE_FACES) }
11
+ end
12
+
13
+ def self.reroll(keepers)
14
+ new_roll = keepers + roll(MAX_DIE_COUNT-keepers.count)
15
+ new_roll[0..(MAX_DIE_COUNT-1)]
16
+ end
17
+ end
18
+
19
+
@@ -0,0 +1,5 @@
1
+ module Yahtzee
2
+ module Errors
3
+ class NotAScoreboardPlacementError < Exception; end
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ require 'yahtzee'
2
+ require 'dice'
3
+ require 'round'
4
+ require 'scoring'
5
+
6
+ # This module is the imperative shell of sorts. It provides
7
+ # the 'DSL' of the Yahtzee library and is mostly a wrapper
8
+ # for internals, 'gluing' the application together from a
9
+ # bunch of functions into a cohesive playable game.
10
+ #
11
+ module Yahtzee
12
+ module Game
13
+ include Yahtzee::Errors
14
+ include Yahtzee::Scoring
15
+ include Yahtzee::Dice
16
+
17
+ module_function
18
+
19
+ def first_roll
20
+ Dice.roll(5)
21
+ end
22
+
23
+ def second_roll(keepers)
24
+ Dice.reroll(keepers)
25
+ end
26
+ # tricky - todo: alias_method :third_roll, :second_roll
27
+
28
+ def third_roll(keepers)
29
+ Dice.reroll(keepers)
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ require 'yahtzee'
2
+ require 'dice'
3
+
4
+ # I will need some kind of UI, Yahtzee::Probability aids in
5
+ # the decisions an AI will have to make based on their score
6
+ # card, the dice, and the probability vs possible score
7
+ module Yahtzee::Probability
8
+ module_function
9
+
10
+ def small_straight(dice, rolls_left)
11
+ possibilities = dice.sort.each_cons(3).select do |a,b,c|
12
+ a+1==b && b+1==c
13
+ end.flatten
14
+ straight_possibility_from(dice, possibilities, rolls_left)
15
+ end
16
+
17
+ def large_straight(dice, rolls_left)
18
+ possibilities = dice.sort.each_cons(4).select do |a,b,c,d|
19
+ a+1==b && b+1==c && c+1==d
20
+ end.flatten
21
+ straight_possibility_from(dice, possibilities, rolls_left)
22
+ end
23
+
24
+ private
25
+
26
+ def self.straight_possibility_from(dice, possibilities, rolls_left)
27
+ dice_left = (dice-possibilities).count
28
+ possible_faces = dice_left*Yahtzee::Dice::DIE_FACES
29
+ rolls_left.to_f/possible_faces.to_f
30
+ end
31
+
32
+ end
@@ -0,0 +1,11 @@
1
+ require 'yahtzee'
2
+
3
+ module Yahtzee
4
+ class Round
5
+
6
+ def initialize(&block)
7
+ block.call
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ require 'yahtzee'
2
+
3
+ # Immutable via API, I will work on freezing this later,
4
+ # but notice that when you 'save' you are not altering
5
+ # THIS instance, you return a new instance with the old
6
+ # and the new attributes
7
+ module Yahtzee
8
+ SCORINGS = [:aces, :twos, :threes,
9
+ :fours, :fives, :sixes,
10
+ :upper_subtotal, :upper_total,
11
+ :small_straight, :large_straight,
12
+ :full_house, :three_of_a_kind, :four_of_a_kind,
13
+ :yahtzee, :chance, :bonus_yahtzee_1,
14
+ :bonus_yahtzee_2, :bonus_yahtzee_3,
15
+ :lower_subtotal, :game_total]
16
+
17
+ class ScoreCard < Struct.new(*SCORINGS)
18
+ def initialize(attrs={})
19
+ super *attrs.values_at(*self.class.members)
20
+ end
21
+
22
+ def to_hash
23
+ Yahtzee::SCORINGS.reduce({}) do |hash, attr|
24
+ hash.merge(attr => send(attr))
25
+ end
26
+ end
27
+
28
+ def self.upper_keys
29
+ [:aces, :twos, :threes, :fours, :fives, :sixes]
30
+ end
31
+
32
+ def self.lower_keys
33
+ [:yahtzee, :bonus_yahtzee_1, :bonus_yahtzee_2,
34
+ :bonus_yahtzee_3, :three_of_a_kind,
35
+ :four_of_a_kind, :full_house, :small_straight,
36
+ :large_straight, :chance]
37
+ end
38
+
39
+
40
+ def self.persist(score_card)
41
+ ->(placement, value) {
42
+ old_values = score_card.to_hash
43
+ new_values = { placement => value }
44
+ score_card = new(old_values.merge(new_values))
45
+ }
46
+ end
47
+
48
+ def self.placement_keys
49
+ Yahtzee::SCORINGS - meta_keys
50
+ end
51
+
52
+ def self.meta_keys
53
+ [:bonus_yahtzee_1, :bonus_yahtzee_2,
54
+ :bonus_yahtzee_3, :upper_subtotal,
55
+ :upper_total, :lower_subtotal,
56
+ :game_total]
57
+ end
58
+ end
59
+ end