rachinations 0.0.3 → 0.0.4

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