rachinations 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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