rachinations 0.0.3 → 0.0.4

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -16
  3. data/Gemfile.lock +2 -0
  4. data/lib/rachinations/domain/diagrams/diagram.rb +46 -14
  5. data/lib/rachinations/domain/edges/edge.rb +34 -20
  6. data/lib/rachinations/domain/modules/common/hash_init.rb +2 -1
  7. data/lib/rachinations/domain/modules/common/invariant.rb +2 -2
  8. data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +22 -0
  9. data/lib/rachinations/domain/nodes/converter.rb +5 -5
  10. data/lib/rachinations/domain/nodes/gate.rb +77 -0
  11. data/lib/rachinations/domain/nodes/node.rb +69 -36
  12. data/lib/rachinations/domain/nodes/pool.rb +121 -74
  13. data/lib/rachinations/domain/nodes/resourceful_node.rb +0 -1
  14. data/lib/rachinations/domain/nodes/sink.rb +3 -0
  15. data/lib/rachinations/domain/nodes/source.rb +3 -2
  16. data/lib/rachinations/domain/resource_bag.rb +3 -4
  17. data/lib/rachinations/dsl/bad_dsl.rb +2 -0
  18. data/lib/rachinations/dsl/bootstrap.rb +59 -0
  19. data/lib/rachinations/dsl/diagram_shorthand_methods.rb +107 -0
  20. data/lib/rachinations/dsl/helpers/parser.rb +170 -0
  21. data/lib/rachinations/extras/constant_hash.rb +25 -0
  22. data/lib/rachinations/extras/fifo.rb +25 -19
  23. data/lib/rachinations/helpers/edge_helper.rb +40 -0
  24. data/lib/rachinations/utils/string_helper.rb +7 -0
  25. data/lib/rachinations/version.rb +1 -1
  26. data/lib/rachinations.rb +13 -5
  27. data/rachinations.gemspec +3 -2
  28. data/testing/simulations/modelo1.rb +1 -1
  29. data/testing/simulations/sequencial.rb +1 -1
  30. data/testing/simulations/sobonito.rb +1 -1
  31. data/testing/simulations/sobonitowhile.rb +1 -1
  32. data/testing/simulations/whatIwish1.rb +2 -2
  33. data/testing/spec/canon/conditions_spec.rb +3 -4
  34. data/testing/spec/converter_spec.rb +3 -4
  35. data/testing/spec/diagram_spec.rb +293 -238
  36. data/testing/spec/edge_spec.rb +28 -14
  37. data/testing/spec/gate_spec.rb +34 -0
  38. data/testing/spec/pool_spec.rb +8 -10
  39. data/testing/spec/release1/dsl_spec.rb +204 -0
  40. data/testing/spec/spec_helper.rb +1 -0
  41. data/testing/spec/xexeo_spec.rb +39 -40
  42. metadata +30 -8
  43. data/lib/rachinations/domain/edges/random_edge.rb +0 -4
  44. data/lib/rachinations/dsl/dsl.rb +0 -63
  45. data/testing/spec/canon/trigger_spec.rb +0 -128
@@ -0,0 +1,170 @@
1
+ require_relative '../../domain/modules/common/refiners/proc_convenience_methods'
2
+ require_relative '../../extras/constant_hash'
3
+
4
+ module DSL
5
+
6
+ # This module helps with parsing arguments used in the DiagramShorthandMethods
7
+ module Parser
8
+ using ProcConvenienceMethods
9
+
10
+ ConstantHash = ::Extras::ConstantHash
11
+
12
+ # these patterns (*_EXPR) define what each argument should look like
13
+
14
+ IDENTIFIER_EXPR = proc { |arg| arg.is_a?(String) && valid_name?(arg) }
15
+
16
+ INITIAL_VALUE_EXPR = proc { |arg| arg.is_a? Fixnum }
17
+
18
+ MODE_EXPR = proc { |arg| [:pull_any, :pull_all, :push_any, :push_all].include? arg }
19
+
20
+ ACTIVATION_EXPR = proc { |arg| [:automatic, :passive, :start].include? arg }
21
+
22
+ PROC_EXPR = proc { |arg| arg.is_a? Proc }
23
+
24
+ # returns true for integers or floats
25
+ LABEL_EXPR = proc { |arg| arg.is_a? Numeric }
26
+
27
+ # Parse an arbitrary list of arguments and returns a well-formed
28
+ # Hash which can then be used as argument to method add_node!
29
+ # @param [Array] arguments an array of arguments.
30
+ # @return [Hash] a well-formed hash
31
+ def self.parse_arguments(arguments)
32
+ arguments.inject(ConstantHash.new) do |accumulator, arg|
33
+
34
+ # named parameters are expressed are hashes
35
+ # and all arguments can (also) be passed as named parameters
36
+ if arg.is_a? Hash
37
+ if arg.has_key? :condition
38
+ if PROC_EXPR.match? arg[:condition]
39
+ accumulator[:condition] = arg[:condition]
40
+ else
41
+ raise BadDSL.new
42
+ end
43
+ end
44
+
45
+ if arg.has_key? :triggered_by
46
+ if IDENTIFIER_EXPR.match? arg[:triggered_by]
47
+ accumulator[:triggered_by] = arg[:triggered_by]
48
+ else
49
+ raise BadDSL.new
50
+ end
51
+ end
52
+
53
+ if arg.has_key? :activation
54
+ if ACTIVATION_EXPR.match? arg[:activation]
55
+ accumulator[:activation] = arg[:activation]
56
+ else
57
+ raise BadDSL.new
58
+ end
59
+ end
60
+
61
+ if arg.has_key? :initial_value
62
+ if INITIAL_VALUE_EXPR.match? arg[:initial_value]
63
+ accumulator[:initial_value] = arg[:initial_value]
64
+ else
65
+ raise BadDSL.new
66
+ end
67
+ end
68
+
69
+ if arg.has_key? :mode
70
+ if MODE_EXPR.match? arg[:mode]
71
+ accumulator[:mode] = arg[:mode]
72
+ else
73
+ raise BadDSL.new
74
+ end
75
+ end
76
+
77
+ else
78
+ if IDENTIFIER_EXPR.match?(arg) # a node's name, if present, is always the first argument
79
+ accumulator[:name] = arg
80
+ elsif INITIAL_VALUE_EXPR.match?(arg)
81
+ accumulator[:initial_value] = arg
82
+ elsif MODE_EXPR.match?(arg)
83
+ accumulator[:mode] = arg
84
+ elsif ACTIVATION_EXPR.match?(arg)
85
+ accumulator[:activation] = arg
86
+ else
87
+ raise BadDSL, "Argument #{arg} doesn't fit any known signature"
88
+ end
89
+ end
90
+ # passing the accumulator onto the next iteration
91
+ accumulator
92
+ end
93
+ end
94
+
95
+ def self.parse_gate_arguments(arguments)
96
+
97
+ # mode is always pull_any so user can't choose it
98
+ # initial_value makes no sense for gates either
99
+ arguments.inject(ConstantHash.new) do |accumulator, arg|
100
+ if arg.is_a? Hash
101
+ if arg.has_key? :condition
102
+ accumulator[:condition] = arg[:condition] if PROC_EXPR.match?(arg[:condition])
103
+ elsif arg.has_key? :triggered_by
104
+ accumulator[:triggered_by] = arg[:triggered_by] if IDENTIFIER_EXPR.match?(arg[:triggered_by])
105
+ else
106
+ ra
107
+ end
108
+ else
109
+ if IDENTIFIER_EXPR.match?(arg)
110
+ accumulator[:name] = arg
111
+ elsif ACTIVATION_EXPR.match?(arg)
112
+ accumulator[:activation] = arg
113
+ else
114
+ raise BadDSL, "Argument #{arg} doesn't fit any known signature"
115
+ end
116
+ end
117
+ accumulator
118
+ end
119
+
120
+ end
121
+
122
+ def self.parse_edge_arguments(arguments)
123
+
124
+ arguments.inject(ConstantHash.new) do |accumulator, arg|
125
+ if IDENTIFIER_EXPR.match?(arg)
126
+ accumulator[:name] = arg
127
+ elsif LABEL_EXPR.match?(arg)
128
+ accumulator[:label]=arg
129
+ elsif arg.is_a? Hash
130
+
131
+ if arg.has_key? :from
132
+ accumulator[:from] = arg[:from]
133
+ end
134
+ if arg.has_key? :to
135
+ accumulator[:to] = arg[:to]
136
+ end
137
+ if arg.has_key? :label
138
+ accumulator[:label] = arg[:label]
139
+ end
140
+ else
141
+ raise BadDSL, "Argument #{arg} doesn't fit any known signature."
142
+ end
143
+ accumulator
144
+ end
145
+
146
+ end
147
+
148
+ # Used to validate that a string is a valid name for diagram components
149
+ #
150
+ # @param [String] text the name we want to validate
151
+ # @raise [BadDSL] if given text is not a valid name
152
+ # @return [String] the text itself, but only if it's valid. Otherwise
153
+ # an exception will be raised.
154
+ def self.validate_name!(text)
155
+ if StringHelper.valid_ruby_variable_name?(text)
156
+ text
157
+ else
158
+ raise BadDSL, "Invalid name: '#{text}'"
159
+ end
160
+ end
161
+
162
+ # @param [String] text the name we want to validate
163
+ # @return [Boolean] whether or not given text is a valid name for diagram elements
164
+ def self.valid_name?(text)
165
+ StringHelper.valid_ruby_variable_name?(text)
166
+ end
167
+
168
+ end
169
+
170
+ end
@@ -0,0 +1,25 @@
1
+ module Extras
2
+
3
+ # A normal hash except that it raises RuntimeErrors
4
+ # if a key gets assigned twice
5
+ class ConstantHash < Hash
6
+
7
+ def []=(key, value)
8
+
9
+ if has_key?(key)
10
+ raise RuntimeError, "key :#{key} has already been set"
11
+ end
12
+
13
+ super
14
+ end
15
+
16
+ def store(key, value)
17
+ if has_key?(key)
18
+ raise RuntimeError, "key :#{key} has already been set"
19
+ end
20
+
21
+ super
22
+ end
23
+
24
+ end
25
+ end
@@ -1,27 +1,33 @@
1
1
  require 'forwardable'
2
2
 
3
- class Fifo
4
- extend Forwardable
3
+ module Extras
5
4
 
6
- def_delegators :@store, :length
5
+ # A class that provides a queue-like (first-in, first-out)
6
+ # structure to clients
7
+ class Fifo
8
+ extend Forwardable
7
9
 
8
- def initialize
9
- @store = Array.new
10
- end
10
+ def_delegators :@store, :length
11
11
 
12
- # Puts an object into the queue
13
- #
14
- # @param [Object] obj
15
- # @return [Fifo] The queue itself.
16
- def put!(obj)
17
- @store.push(obj)
18
- self
19
- end
12
+ def initialize
13
+ @store = Array.new
14
+ end
20
15
 
21
- # Takes the least recently added element.
22
- #
23
- # @return [Object] whatever was stored in this queue.
24
- def take!
25
- @store.shift
16
+ # Puts an object into the queue
17
+ #
18
+ # @param [Object] obj
19
+ # @return [Fifo] The queue itself.
20
+ def put!(obj)
21
+ @store.push(obj)
22
+ self
23
+ end
24
+
25
+ # Takes the least recently added element.
26
+ #
27
+ # @return [Object] whatever was stored in this queue.
28
+ def take!
29
+ @store.shift
30
+ end
26
31
  end
32
+
27
33
  end
@@ -0,0 +1,40 @@
1
+ module Helpers
2
+ module EdgeHelper
3
+
4
+ # Returns true if all edges passed as arguments can execute a push
5
+ # when called in the same round. The difference between calling this
6
+ # method and calling Edge#test_ping? on each edge is that this method
7
+ # knows that each edge removes at least one Resource when it performs
8
+ # a push.
9
+ # @param [Array<Edge>] edges the edges
10
+ # @param [Boolean] require_all whether to require that the maximum amount
11
+ # of resources supported by each edge (i.e. its label) be available in
12
+ # order to succeed.
13
+ # @return [Boolean]
14
+ def self.all_can_push?(edges, require_all: false)
15
+ initial = {
16
+ partial_success: true,
17
+ number_of_resources: 0
18
+ }
19
+
20
+ raise NotImplementedError, 'only require_all is implemented so far' unless require_all
21
+
22
+ final = edges.inject(initial) do |accumulator, edge|
23
+ accumulator[:number_of_resources] += edge.label
24
+
25
+ resources_available = edge.from.resource_count(expr: edge.push_expression)
26
+
27
+ if resources_available >= accumulator[:number_of_resources]
28
+ accumulator[:partial_success] &&= true
29
+ else
30
+ accumulator[:partial_success] &&= false
31
+ end
32
+ accumulator
33
+ end
34
+
35
+ final[:partial_success]
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ module StringHelper
2
+
3
+ def self.valid_ruby_variable_name?(str)
4
+ (/^[a-z_][a-zA-Z_0-9]*$/ =~ str) == 0
5
+ end
6
+
7
+ end
@@ -1,3 +1,3 @@
1
1
  module Rachinations
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/rachinations.rb CHANGED
@@ -2,23 +2,26 @@ 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
-
6
-
7
5
  require 'rachinations/domain/modules/common/refiners/proc_convenience_methods'
6
+ require 'rachinations/domain/modules/common/refiners/number_modifiers'
7
+
8
8
  require 'rachinations/extras/fifo'
9
9
 
10
10
  require 'rachinations/domain/diagrams/diagram'
11
11
  require 'rachinations/domain/diagrams/verbose_diagram'
12
- require 'rachinations/dsl/dsl'
12
+ require 'rachinations/dsl/diagram_shorthand_methods'
13
+ require 'rachinations/dsl/bootstrap'
13
14
  require 'rachinations/domain/strategies/strategy'
14
15
  require 'rachinations/domain/strategies/valid_types'
15
- require 'rachinations/domain/edges/random_edge'
16
16
  require 'rachinations/domain/edges/edge'
17
+
17
18
  require 'rachinations/domain/exceptions/no_elements_of_given_type'
18
19
  require 'rachinations/domain/exceptions/unsupported_type_error'
19
20
  require 'rachinations/domain/exceptions/bad_options'
20
21
  require 'rachinations/domain/exceptions/no_elements_matching_condition_error'
21
22
  require 'rachinations/domain/exceptions/no_elements_found'
23
+ require 'rachinations/dsl/bad_dsl'
24
+
22
25
  require 'rachinations/domain/nodes/node'
23
26
  require 'rachinations/domain/nodes/resourceful_node'
24
27
  require 'rachinations/domain/nodes/pool'
@@ -33,4 +36,9 @@ require 'rachinations/domain/node_collection'
33
36
  require 'rachinations/domain/resource_bag'
34
37
 
35
38
 
36
- include DSL
39
+ # users can call .percent on integers
40
+ #using NumberModifiers
41
+
42
+ # users can use the dsl to create diagrams
43
+ include DSL::Bootstrap
44
+ using DSL::DiagramShorthandMethods
data/rachinations.gemspec CHANGED
@@ -9,9 +9,9 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Felipe Almeida"]
10
10
  spec.email = ["falmeida1988@gmail.com"]
11
11
  spec.summary = %q{Ruby port for Dr. J. Dormans' Machinations Game Mechanics Diagrams.}
12
- spec.description = %q{This project provides a Ruby-based DSL to enable game designers to
12
+ spec.description = %q{This project provides a Ruby-based DiagramShorthandMethods to enable game designers to
13
13
  design and also test tentative game designs and/or prototypes}
14
- spec.homepage = ""
14
+ spec.homepage = "https://github.com/queirozfcom/rachinations"
15
15
  spec.license = "MIT"
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0")
@@ -30,5 +30,6 @@ design and also test tentative game designs and/or prototypes}
30
30
 
31
31
  spec.add_dependency "activesupport","3.0.0"
32
32
  spec.add_dependency "i18n","0.6.11"
33
+ spec.add_dependency "weighted_distribution"
33
34
 
34
35
  end
@@ -4,7 +4,7 @@ require_relative '../../domain/nodes/pool'
4
4
  require_relative '../../domain/nodes/source'
5
5
  require_relative '../../domain/edges/edge'
6
6
 
7
- include DSL
7
+ include DiagramShorthandMethods
8
8
 
9
9
  n=diagram 'test_diagram' , :verbose do
10
10
  node 'source', Source
@@ -2,7 +2,7 @@ require_relative '../../domain/diagrams/diagram'
2
2
  require_relative '../../dsl/dsl'
3
3
  require_relative '../../domain/nodes/pool'
4
4
  require_relative '../../domain/edges/edge'
5
- include DSL
5
+ include DiagramShorthandMethods
6
6
 
7
7
  n=diagram 'test_diagram', :verbose do
8
8
  node 'p1', Pool, mode: :push, activation: :automatic, initial_value: 8
@@ -3,7 +3,7 @@ require_relative '../../dsl/dsl'
3
3
  require_relative '../../domain/nodes/pool'
4
4
  require_relative '../../domain/edges/edge'
5
5
 
6
- include DSL
6
+ include DiagramShorthandMethods
7
7
 
8
8
  n=diagram 'test_diagram',:verbose do
9
9
  node 'p1', Pool, mode: :push, activation: :automatic, initial_value: 8
@@ -2,7 +2,7 @@ require_relative '../../domain/diagrams/diagram'
2
2
  require_relative '../../dsl/dsl'
3
3
  require_relative '../../domain/nodes/pool'
4
4
  require_relative '../../domain/edges/edge'
5
- include DSL
5
+ include DiagramShorthandMethods
6
6
 
7
7
  n=diagram 'test_diagram', :verbose do
8
8
  node 'p1', Pool, mode: :push, activation: :automatic, initial_value: 8
@@ -1,6 +1,6 @@
1
1
  require_relative '../domain/diagram'
2
- require_relative '../dsl/dsl.rb'
3
- include DSL
2
+ require_relative '../dsl/diagram_shorthand_methods.rb'
3
+ include DiagramShorthandMethods
4
4
 
5
5
  n=diagram 'test_diagram' do
6
6
  node 'source', Source
@@ -15,14 +15,13 @@ describe 'Nodes that can be given conditions' do
15
15
  :initial_value => 0
16
16
  }
17
17
 
18
-
19
18
  d.add_edge! Edge, {
20
19
  :name => 'connector',
21
20
  :from => 'source',
22
21
  :to => 'deposit'
23
22
  }
24
23
 
25
- d.get_node('source').attach_condition(lambda { false })
24
+ d.get_node('source').attach_condition{ false }
26
25
 
27
26
  d.run!(10)
28
27
 
@@ -53,7 +52,7 @@ describe 'Nodes that can be given conditions' do
53
52
  :to => 'deposit'
54
53
  }
55
54
 
56
- d.get_node('source').attach_condition(lambda { d.get_node('deposit').resource_count < 3 })
55
+ d.get_node('source').attach_condition { d.get_node('deposit').resource_count < 3 }
57
56
 
58
57
  d.run!(10)
59
58
 
@@ -82,7 +81,7 @@ describe 'Nodes that can be given conditions' do
82
81
  :to => 'deposit'
83
82
  }
84
83
 
85
- d.get_node('deposit').attach_condition(lambda { d.get_node('deposit').resource_count < 3 })
84
+ d.get_node('deposit').attach_condition { d.get_node('deposit').resource_count < 3 }
86
85
 
87
86
  d.run!(10)
88
87
 
@@ -96,7 +96,6 @@ describe Converter do
96
96
 
97
97
  expect(@c).to receive(:in_conditions_met?).and_return(true)
98
98
 
99
-
100
99
  expect(@edge_out).to receive(:test_push?).and_return(true)
101
100
  expect(@edge_out).to receive(:push_expression).and_return(proc{true})
102
101
  expect(@edge_out).to receive(:push!)
@@ -119,13 +118,13 @@ describe Converter do
119
118
 
120
119
  it 'pushes and pulls if all edges (incoming and outgoing) can push and pull, respectively' do
121
120
 
122
- expect(@edge_in).to receive(:test_pull?).with(true).and_return(true)
121
+ expect(@edge_in).to receive(:test_pull?).with(require_all:true).and_return(true)
123
122
  expect(@edge_in).to receive(:pull!)
124
123
 
125
- expect(@edge_in2).to receive(:test_pull?).with(true).and_return(true)
124
+ expect(@edge_in2).to receive(:test_pull?).with(require_all:true).and_return(true)
126
125
  expect(@edge_in2).to receive(:pull!)
127
126
 
128
- expect(@edge_out).to receive(:test_push?).with(true).and_return(true)
127
+ expect(@edge_out).to receive(:test_push?).with(require_all:true).and_return(true)
129
128
  expect(@edge_out).to receive(:push!)
130
129
 
131
130