rachinations 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -1
- data/README.md +184 -5
- data/lib/rachinations.rb +5 -4
- data/lib/rachinations/domain/diagrams/diagram.rb +35 -24
- data/lib/rachinations/domain/exceptions/bad_config.rb +1 -1
- data/lib/rachinations/domain/{exceptions → modules/common}/bad_options.rb +0 -0
- data/lib/rachinations/domain/modules/common/hash_init.rb +1 -0
- data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +2 -1
- data/lib/rachinations/domain/modules/diagrams/verbose.rb +1 -1
- data/lib/rachinations/domain/nodes/gate.rb +23 -27
- data/lib/rachinations/domain/nodes/node.rb +4 -20
- data/lib/rachinations/domain/nodes/pool.rb +2 -3
- data/lib/rachinations/domain/nodes/resourceful_node.rb +4 -3
- data/lib/rachinations/domain/nodes/sink.rb +1 -1
- data/lib/rachinations/dsl/bad_dsl.rb +3 -0
- data/lib/rachinations/dsl/bootstrap.rb +10 -8
- data/lib/rachinations/dsl/diagram_shorthand_methods.rb +10 -5
- data/lib/rachinations/dsl/helpers/parser.rb +83 -49
- data/lib/rachinations/helpers/edge_helper.rb +105 -8
- data/lib/rachinations/utils/math_utils.rb +83 -0
- data/lib/rachinations/utils/string_utils.rb +8 -0
- data/lib/rachinations/version.rb +1 -1
- data/rachinations.gemspec +2 -1
- data/testing/spec/diagram_spec.rb +78 -17
- data/testing/spec/gate_spec.rb +223 -2
- data/testing/spec/non_deterministic_diagram_spec.rb +1 -1
- data/testing/spec/release1/dsl_spec.rb +100 -3
- data/testing/spec/release1/individual_cases_spec.rb +39 -0
- data/testing/spec/release1/monografia_spec.rb +69 -0
- metadata +24 -5
- data/lib/rachinations/utils/string_helper.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d453a7d9164b1dd29813252604b256fc8219f3c
|
4
|
+
data.tar.gz: 4ac7305c010e2b43d2488bceaab5aecf30f2db72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
158
|
-
|
159
|
-
|
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
|
173
|
+
def maximum_round?(round_no)
|
174
|
+
round_no > @max_iterations
|
175
|
+
end
|
167
176
|
|
168
|
-
|
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
|
-
|
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
|
File without changes
|
@@ -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,
|
27
|
+
def put_resource!(res, from_edge)
|
23
28
|
|
24
|
-
|
25
|
-
|
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
|
-
#
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
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: :
|
74
|
+
mode: :deterministic,
|
79
75
|
types: []
|
80
76
|
}
|
81
77
|
end
|