rachinations 0.0.7 → 0.0.8

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -1
  3. data/README.md +184 -5
  4. data/lib/rachinations.rb +5 -4
  5. data/lib/rachinations/domain/diagrams/diagram.rb +35 -24
  6. data/lib/rachinations/domain/exceptions/bad_config.rb +1 -1
  7. data/lib/rachinations/domain/{exceptions → modules/common}/bad_options.rb +0 -0
  8. data/lib/rachinations/domain/modules/common/hash_init.rb +1 -0
  9. data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +2 -1
  10. data/lib/rachinations/domain/modules/diagrams/verbose.rb +1 -1
  11. data/lib/rachinations/domain/nodes/gate.rb +23 -27
  12. data/lib/rachinations/domain/nodes/node.rb +4 -20
  13. data/lib/rachinations/domain/nodes/pool.rb +2 -3
  14. data/lib/rachinations/domain/nodes/resourceful_node.rb +4 -3
  15. data/lib/rachinations/domain/nodes/sink.rb +1 -1
  16. data/lib/rachinations/dsl/bad_dsl.rb +3 -0
  17. data/lib/rachinations/dsl/bootstrap.rb +10 -8
  18. data/lib/rachinations/dsl/diagram_shorthand_methods.rb +10 -5
  19. data/lib/rachinations/dsl/helpers/parser.rb +83 -49
  20. data/lib/rachinations/helpers/edge_helper.rb +105 -8
  21. data/lib/rachinations/utils/math_utils.rb +83 -0
  22. data/lib/rachinations/utils/string_utils.rb +8 -0
  23. data/lib/rachinations/version.rb +1 -1
  24. data/rachinations.gemspec +2 -1
  25. data/testing/spec/diagram_spec.rb +78 -17
  26. data/testing/spec/gate_spec.rb +223 -2
  27. data/testing/spec/non_deterministic_diagram_spec.rb +1 -1
  28. data/testing/spec/release1/dsl_spec.rb +100 -3
  29. data/testing/spec/release1/individual_cases_spec.rb +39 -0
  30. data/testing/spec/release1/monografia_spec.rb +69 -0
  31. metadata +24 -5
  32. data/lib/rachinations/utils/string_helper.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f121ae6b88636bb8f2d61ce349052e5eaaab765a
4
- data.tar.gz: 80b8154cb68f75059ed98c866043b052c1271ac0
3
+ metadata.gz: 7d453a7d9164b1dd29813252604b256fc8219f3c
4
+ data.tar.gz: 4ac7305c010e2b43d2488bceaab5aecf30f2db72
5
5
  SHA512:
6
- metadata.gz: 555791bf758a0458b7cfc3b1c71287c33b54ce296d957878f4c4d236fb0186a8b1aaf036529119b5a85bd40eeeeacf152e7bf2df87590040f75a89330c52e067
7
- data.tar.gz: 1443bdad6609c32eb441b22e686c46b04c531808f190301ed0de56d309730ff2b94cd7b47dc4244e252594188fe9b2939caad027c72168a3f9512cd0a9a2fd9b
6
+ metadata.gz: 79580ad9cd32113ee03fb3ca7dbe6d6a64cd3de6f40eb98823a8a4337931c34634046f7515071b67c38c82fc66cdbd7cbf29d2e1ed4a66f7c5ffef13ec719354
7
+ data.tar.gz: e565c119969a4c534adf4b7c6843d9cea5e5e5ef5548438e58152c40c6418f1118cb862b128ec6924fa19e39d05ed4da1186a3fdbc9fa0c3aa17a38750e7f97b
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rachinations (0.0.6)
4
+ rachinations (0.0.7)
5
5
  activesupport (= 3.0.0)
6
+ fraction (= 0.3.2)
6
7
  i18n (= 0.6.11)
7
8
  weighted_distribution (= 1.0.0)
8
9
 
@@ -22,6 +23,7 @@ GEM
22
23
  docile (1.1.5)
23
24
  ffi (1.9.3-x64-mingw32)
24
25
  ffi (1.9.3-x86-mingw32)
26
+ fraction (0.3.2)
25
27
  i18n (0.6.11)
26
28
  mime-types (2.3)
27
29
  minitest (5.4.0)
data/README.md CHANGED
@@ -1,18 +1,197 @@
1
- rachinations
1
+ Rachinations
2
2
  ====================
3
3
  [![Build Status](https://travis-ci.org/queirozfcom/rachinations.svg?branch=master)](https://travis-ci.org/queirozfcom/rachinations?branch=master)
4
4
  [![Code Climate](https://codeclimate.com/github/queirozfcom/rachinations.png)](https://codeclimate.com/github/queirozfcom/rachinations)
5
5
  [![Coverage Status](https://coveralls.io/repos/queirozfcom/rachinations/badge.png?branch=master)](https://coveralls.io/r/queirozfcom/rachinations?branch=master)
6
6
  [![Gem Version](https://badge.fury.io/rb/rachinations.svg)](http://badge.fury.io/rb/rachinations)
7
7
 
8
- ### Introduction
9
-
10
8
  This is a port of Dr. J. Dormans' [Machinations framework](http://www.jorisdormans.nl/machinations/) into Ruby.
11
9
 
12
10
  It provides a Ruby-based DSL to enable game designers to create and also test tentative game designs and/or prototypes.
13
11
 
14
- ### Contents
12
+ ## Contents
15
13
 
16
14
  - Classes to model the domain
17
15
  - Tests
18
- - A simple DSL (Domain-specific language) whose objective is to enable anyone to write Machinations diagrams, run them, obtain metrics, compose subdiagrams and so on.
16
+ - A simple DSL (Domain-specific language) whose objective is to enable anyone to write Machinations diagrams, run them, obtain metrics, compose subdiagrams and so on.
17
+
18
+ ## Installation Guide
19
+
20
+ Rachinations is written in Ruby so you need to have Ruby installed on your system. You only need 5 minutes to get it to work:
21
+
22
+ **Linux**
23
+
24
+ The best way to install Ruby on a Linux-based machine is probably RVM. [Instructions on how to install RVM on Linux here](http://queirozf.com/entries/tutorial-and-examples-on-how-to-use-rvm-on-linux)
25
+
26
+ Once Ruby is installed, you just need to install the rachinations **gem**. The process is straightforward:
27
+
28
+ ```
29
+ $ gem install rachinations
30
+ ```
31
+
32
+ **Windows**
33
+
34
+ - **Installation**
35
+
36
+ On Windows, the best way to get up and running with Ruby is probably using the [RubyInstaller for Windows](http://rubyinstaller.org/)
37
+
38
+ Please note that Rachinations requires at least Ruby **version 2.1** to work.
39
+
40
+ If you have never used Ruby before, I recommend you tick the following two boxes, as per the following image:
41
+
42
+ ![installing_ruby_on_windows](http://i.imgur.com/Y0u1ZzN.png)
43
+
44
+ - **Veryfing that the installation worked**
45
+
46
+ Once Ruby is installed, open a **command prompt** and type `ruby -v` just to see if everything worked.
47
+
48
+ You should see something like this (details may vary slightly)
49
+
50
+ ```
51
+ > ruby -v
52
+ ruby 2.1.5p273 (2014-11-13 revision 48405) [x64-mingw32]
53
+ ```
54
+
55
+ - **Configuring rubygems and installing the library**
56
+
57
+ Once that's done, we'll configure `gem` (Ruby's package manager) to address a well known problem that has to do with certificates on Windows. More info [here](http://stackoverflow.com/questions/9962051/could-not-find-a-valid-gem-in-any-repository-rubygame-and-others) and [here](http://help.rubygems.org/discussions/problems/19761-could-not-find-a-valid-gem).
58
+
59
+ On the command prompt, do this:
60
+
61
+ ```
62
+ > gem sources -r https://rubygems.org
63
+ ```
64
+ and
65
+
66
+ ```
67
+ > gem sources -a http://rubygems.org
68
+ https://rubygems.org is recommended for security
69
+
70
+ Do you want to add this insecure source? [yn] y
71
+ http://rubygems.org added to sources
72
+ ```
73
+
74
+ After you've done the last step (which adds a new source for gems to be fetched from), then you can install the gem proper:
75
+
76
+ ```
77
+ > gem install rachinations
78
+ ```
79
+ (you might see a few error messages, but don't worry)
80
+
81
+ ## Usage
82
+
83
+ All you need to do is write your diagram in a file whose name ends in `.rb` and run it using the `ruby` command.
84
+
85
+ ### Examples
86
+
87
+ - **Simplest possible example**
88
+
89
+ ```ruby
90
+ require 'rachinations'
91
+
92
+ # this is a simple diagram with a single pool with
93
+ # 5 resources
94
+ d=diagram 'simplest_diagram' do
95
+ pool initial_value: 5
96
+ end
97
+
98
+ # and execute it for 10 rounds
99
+ d.run 10
100
+ ```
101
+
102
+ Save this code into a file (say, `static_diagram.rb`) and run it like this:
103
+
104
+ ```
105
+ $ ruby static_diagram.rb
106
+ ```
107
+
108
+ - **Example 1**
109
+
110
+ ```ruby
111
+ require 'rachinations'
112
+
113
+ diagram 'example_1' do
114
+ source 's1', :automatic
115
+ pool 'p1'
116
+ pool 'p2', :automatic
117
+ edge from: 's1', to: 'p1'
118
+ edge from: 'p1', to: 'p2'
119
+ end
120
+ ```
121
+
122
+ - **Example 2**
123
+
124
+ ```ruby
125
+ require 'rachinations'
126
+
127
+ diagram 'example_2' do
128
+ source 's1'
129
+ pool 'p1'
130
+ converter 'c1', :automatic
131
+ pool 'p2'
132
+ pool 'p3'
133
+ edge from: 's1', to: 'p1'
134
+ edge from: 'p1', to: 'c1'
135
+ edge from: 'c1', to: 'p2'
136
+ edge from: 'c1', to: 'p3'
137
+ end
138
+ ```
139
+
140
+ - **Example 3**
141
+
142
+ ```ruby
143
+ require 'rachinations'
144
+
145
+ diagram 'example_3' do
146
+ source 's1'
147
+ gate 'g1', :probabilistic
148
+ pool 'p1'
149
+ pool 'p2'
150
+ pool 'p3'
151
+ sink 's2', :automatic, condition: expr{ p2.resource_count > 30 }
152
+ edge from: 's1', to: 'g1'
153
+ edge from: 'g1', to: 'p1'
154
+ edge 2, from: 'g1', to: 'p2'
155
+ edge from: 'g1', to: 'p3'
156
+ edge from: 'p3', to: 's2'
157
+ end
158
+ ```
159
+
160
+ - **Example 4**
161
+
162
+ ```ruby
163
+ require 'rachinations'
164
+
165
+ diagram 'example_4' do
166
+ source 's1'
167
+ pool 'p1', triggers: 's2'
168
+ source 's2', :passive
169
+ pool 'p2'
170
+ edge from: 's1', to: 'p1'
171
+ edge from: 's2', to: 'p2'
172
+ end
173
+ ```
174
+
175
+ - **Example 4, alternative version**
176
+
177
+ This amounts to the same diagram as the one defined in Example 4, but uses a different mechanism for defining triggers between nodes.
178
+
179
+ ```ruby
180
+ require 'rachinations'
181
+
182
+ diagram 'example_4_alternative' do
183
+ source 's1'
184
+ pool 'p1'
185
+ source 's2', :passive, triggered_by: 'p1'
186
+ pool 'p2'
187
+ edge from: 's1', to: 'p1'
188
+ edge from: 's2', to: 'p2'
189
+ end
190
+ ```
191
+
192
+ ### Full DSL specification
193
+ **TODO**
194
+
195
+
196
+
197
+
data/lib/rachinations.rb CHANGED
@@ -2,15 +2,18 @@ lib = File.expand_path('../lib', __FILE__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
  require 'rachinations/version'
4
4
 
5
+ require 'rachinations/utils/math_utils'
6
+ require 'rachinations/utils/string_utils'
7
+
5
8
  require 'rachinations/domain/modules/common/refiners/proc_convenience_methods'
6
9
  require 'rachinations/domain/modules/common/refiners/number_modifiers'
7
10
  require 'rachinations/domain/modules/common/schedulable_tasks'
8
11
 
9
-
10
12
  require 'rachinations/extras/fifo'
11
13
 
12
14
  require 'rachinations/domain/diagrams/diagram'
13
15
  require 'rachinations/domain/diagrams/verbose_diagram'
16
+ require 'rachinations/domain/diagrams/non_deterministic_diagram'
14
17
  require 'rachinations/dsl/diagram_shorthand_methods'
15
18
  require 'rachinations/dsl/bootstrap'
16
19
  require 'rachinations/domain/strategies/strategy'
@@ -19,10 +22,9 @@ require 'rachinations/domain/edges/edge'
19
22
 
20
23
  require 'rachinations/domain/exceptions/no_elements_of_given_type'
21
24
  require 'rachinations/domain/exceptions/unsupported_type_error'
22
- require 'rachinations/domain/exceptions/bad_options'
23
- require 'rachinations/domain/exceptions/bad_config'
24
25
  require 'rachinations/domain/exceptions/no_elements_matching_condition_error'
25
26
  require 'rachinations/domain/exceptions/no_elements_found'
27
+ require 'rachinations/domain/exceptions/bad_config'
26
28
  require 'rachinations/dsl/bad_dsl'
27
29
 
28
30
  require 'rachinations/domain/nodes/node'
@@ -38,7 +40,6 @@ require 'rachinations/domain/edge_collection'
38
40
  require 'rachinations/domain/node_collection'
39
41
  require 'rachinations/domain/resource_bag'
40
42
 
41
-
42
43
  # users can use the dsl to create diagrams
43
44
  include DSL::Bootstrap
44
45
 
@@ -15,9 +15,9 @@ class Diagram
15
15
  @edges = EdgeCollection.new
16
16
  @name = name
17
17
  @max_iterations = 999
18
+ @stop_conditions = []
18
19
  end
19
20
 
20
-
21
21
  def get_node(name)
22
22
 
23
23
  nodes.each do |node|
@@ -96,10 +96,23 @@ class Diagram
96
96
  self
97
97
  end
98
98
 
99
- def run!(rounds=5)
99
+ def add_stop_condition!(message: nil, condition:)
100
+ raise ArgumentError, 'Expression required for stop condition' unless condition.is_a? Proc
101
+ raise ArgumentError, 'Message can be omitted only if there is no other condition' if message.nil? && !(@stop_conditions.empty?)
102
+
103
+ hsh = {
104
+ message: message,
105
+ condition: condition
106
+ }
107
+
108
+ @stop_conditions << hsh
109
+
110
+ end
111
+
112
+ def run!(rounds = max_iterations)
100
113
 
101
114
  run_while! do |i|
102
- i<=rounds
115
+ i <= rounds && no_stop_conditions_met?
103
116
  end
104
117
 
105
118
  end
@@ -115,10 +128,9 @@ class Diagram
115
128
 
116
129
  i=1
117
130
 
118
- # if given condition block turned false, it's time to stop
119
131
  while yield i do
120
132
 
121
- break unless sanity_check? i
133
+ break if maximum_round? i
122
134
 
123
135
  before_round i
124
136
 
@@ -139,7 +151,6 @@ class Diagram
139
151
 
140
152
  end
141
153
 
142
-
143
154
  def resource_count(klass=nil)
144
155
  total=0
145
156
  @nodes.each do |n|
@@ -154,40 +165,40 @@ class Diagram
154
165
  nodes.reduce('') { |carry, n| carry+n.to_s }
155
166
  end
156
167
 
157
- def sanity_check?(round_no)
158
- if round_no > @max_iterations
159
- sanity_check_message
160
- false
161
- else
162
- true
163
- end
168
+ def no_stop_conditions_met?
169
+ @stop_conditions
170
+ .all? { |el| ! el[:condition].call }
164
171
  end
165
172
 
166
- def run_first_round!
173
+ def maximum_round?(round_no)
174
+ round_no > @max_iterations
175
+ end
167
176
 
168
- enabled_nodes.select { |node| node.automatic? || node.start? }.shuffle.each { |node| node.trigger! }
177
+ def run_first_round!
178
+ enabled_nodes
179
+ .select { |node| node.automatic? || node.start? }
180
+ .shuffle
181
+ .each { |node| node.trigger! }
169
182
 
170
183
  commit_nodes!
171
-
172
184
  end
173
185
 
174
186
  def run_round!
175
-
176
- enabled_nodes.select { |node| node.automatic? }.shuffle.each { |node| node.trigger! }
187
+ enabled_nodes
188
+ .select { |node| node.automatic? }
189
+ .shuffle
190
+ .each { |node| node.trigger! }
177
191
 
178
192
  commit_nodes!
179
-
180
193
  end
181
194
 
182
195
  def commit_nodes!
183
196
  #only after all nodes have run do we update the actual resources and changes, to be used in the next round.
184
197
  nodes.shuffle.each { |node| node.commit! }
185
-
186
198
  end
187
199
 
188
200
  def enabled_nodes
189
201
  nodes.select { |node| node.enabled? }
190
-
191
202
  end
192
203
 
193
204
  #template method
@@ -199,15 +210,15 @@ class Diagram
199
210
  end
200
211
 
201
212
  #template method
202
- def before_run;
213
+ def before_run
203
214
  end
204
215
 
205
216
  #template method
206
- def after_run;
217
+ def after_run
207
218
  end
208
219
 
209
220
  #template method
210
- def sanity_check_message;
221
+ def sanity_check_message
211
222
  end
212
223
 
213
224
  end
@@ -1,4 +1,4 @@
1
1
  # this exception should be used to signal that an inconsistent state
2
- # was found, but due to bad initialization, not bad changes in state
2
+ # was found due to bad initialization, not bad changes in state
3
3
  class BadConfig < RuntimeError
4
4
  end
@@ -1,3 +1,4 @@
1
+ require_relative 'bad_options'
1
2
  #use this for classes you want to be able to instantiate
2
3
  #using a hashful of parameters
3
4
  module HashInit
@@ -1,5 +1,6 @@
1
- module NumberModifiers
1
+ # require 'fraction'
2
2
 
3
+ module NumberModifiers
3
4
 
4
5
  # allow user to modify Numbers in order to express
5
6
  # percentages ( 12.percent ) and also fractions, for
@@ -2,7 +2,7 @@ module Verbose
2
2
 
3
3
 
4
4
  def before_run
5
- print "\033[1;32m===== INITIAL STATE =====\e[00m\n\n"
5
+ print "\n\033[1;32m===== INITIAL STATE =====\e[00m\n\n"
6
6
 
7
7
  puts self
8
8
  end
@@ -2,9 +2,13 @@ require_relative '../../domain/nodes/node'
2
2
  require_relative '../../domain/nodes/resourceless_node'
3
3
 
4
4
  require 'weighted_distribution'
5
+ require 'fraction'
5
6
 
6
7
  class Gate < ResourcelessNode
7
8
 
9
+ EdgeHelper = Helpers::EdgeHelper
10
+
11
+ attr_reader :mode, :activation
8
12
 
9
13
  def initialize(hsh={})
10
14
  check_options!(hsh)
@@ -13,55 +17,47 @@ class Gate < ResourcelessNode
13
17
  @diagram = params[:diagram]
14
18
  @name = params[:name]
15
19
  @activation = params[:activation]
20
+ # for gates, :mode has different semantics
16
21
  @mode = params[:mode]
17
22
  @types = get_types(given_types: params[:types])
18
23
 
19
24
  super(hsh)
20
25
  end
21
26
 
22
- def put_resource!(res,edge)
27
+ def put_resource!(res, from_edge)
23
28
 
24
- inv("Edges must be either all defaults or all probabilities") { outgoing_edges.all?{|e| e.label == 1 } || outgoing_edges.all?{|e| e.label.class == Float} }
25
- inv("If probabilities given, their sum must not exceed 1"){ outgoing_edges.reduce(0){|acc,el| acc + el.label } <= 1 || !outgoing_edges.all?{|e| e.label.class == Float} }
29
+ raise BadConfig.new('All outgoing Edges must be of the same kind') unless EdgeHelper.all_labels_of_same_kind?(outgoing_edges)
30
+ raise BadConfig.new('If probabilities are given, they must add up to 1') unless EdgeHelper.labels_valid?(outgoing_edges)
26
31
 
27
- maybe_edge = pick_one(outgoing_edges)
32
+ maybe_edge = EdgeHelper.pick_one(edges: outgoing_edges, mode: mode, index: next_edge_index)
28
33
 
29
- if(maybe_edge.nil?)
30
- # do nothing - resource has 'vanished' - happens every other day
34
+ if (maybe_edge.nil?)
35
+ # no outgoing edges. resource disappears
31
36
  else
32
37
  maybe_edge.push!(res)
33
38
  end
34
39
 
35
40
  end
36
41
 
42
+ def take_resource!(res, edge)
43
+ # no action
44
+ end
45
+
37
46
  def to_s
38
47
  "Gate '#{@name}'\n\n"
39
48
  end
40
49
 
41
50
  private
42
51
 
43
- def pick_one(edges)
44
-
45
- if(edges.all?{|e| e.label == 1})
46
- #edges is probably an enumerable but only arrays can be sample()'d
47
- edges.to_a.sample
48
- elsif(edges.all?{|e| e.label.class == Float})
49
- #{edge=>weight} is the shape required by WeightedRandomizer
50
- weights = edges.reduce(Hash.new){|acc,el| acc[el] = el.label; acc }
51
-
52
- sum = edges.reduce(0){|acc,el|acc + el.label }
52
+ # used to indicate what edge index
53
+ # when in :deterministic mode
54
+ def next_edge_index
55
+ # starting at zero
56
+ @next_edge_index ||= 0
53
57
 
54
- remaining = 1.0 - sum
55
-
56
- #resource 'vanishes'
57
- weights[nil] = remaining
58
-
59
- edge_distribution = WeightedDistribution.new(weights)
60
- edge_distribution.sample
61
- else
62
- raise RuntimeError.new('Invalid setup')
63
- end
58
+ @next_edge_index += 1
64
59
 
60
+ (@next_edge_index - 1)
65
61
  end
66
62
 
67
63
  def options
@@ -75,7 +71,7 @@ class Gate < ResourcelessNode
75
71
  def defaults
76
72
  {
77
73
  activation: :passive,
78
- mode: :push_any,
74
+ mode: :deterministic,
79
75
  types: []
80
76
  }
81
77
  end