lomic 0.0.1
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.
- data/LICENSE +20 -0
- data/Manifest +22 -0
- data/README.rdoc +55 -0
- data/Rakefile +65 -0
- data/TODO +2 -0
- data/VERSION.yml +5 -0
- data/examples/nomic_initial_rules.rb +184 -0
- data/examples/priority.rb +35 -0
- data/examples/simple.rb +15 -0
- data/features/resource.feature +41 -0
- data/features/step_definitions/steps.rb +39 -0
- data/features/support/env.rb +2 -0
- data/features/var_init.feature +14 -0
- data/lib/Lomic.rb +16 -0
- data/lib/lomic/Event.rb +12 -0
- data/lib/lomic/EventEngine.rb +78 -0
- data/lib/lomic/GameState.rb +35 -0
- data/lib/lomic/Lomic.rb +104 -0
- data/lib/lomic/LomicParser.rb +43 -0
- data/lib/lomic/Rule.rb +44 -0
- metadata +119 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Gilbert B Garza
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
LICENSE
|
2
|
+
Manifest
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
TODO
|
6
|
+
examples/nomic_initial_rules.rb
|
7
|
+
examples/priority.rb
|
8
|
+
examples/simple.rb
|
9
|
+
features/event_engine.feature
|
10
|
+
features/resource.feature
|
11
|
+
features/step_definitions/steps.rb
|
12
|
+
features/support/env.rb
|
13
|
+
features/var_init.feature
|
14
|
+
lib/lomic.rb
|
15
|
+
lib/lomic/Event.rb
|
16
|
+
lib/lomic/EventEngine.rb
|
17
|
+
lib/lomic/GameState.rb
|
18
|
+
lib/lomic/Lomic.rb
|
19
|
+
lib/lomic/LomicParser.rb
|
20
|
+
lib/lomic/Rule.rb
|
21
|
+
lomic.gemspec
|
22
|
+
parse.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
= Lomic
|
2
|
+
|
3
|
+
Lomic is a Domain Specific Language (DSL) intended to be used for Pomic, a programming version of the game Nomic.
|
4
|
+
|
5
|
+
== What does Lomic look like?
|
6
|
+
|
7
|
+
Lomic is designed to be expressive in declaring rules for the game Nomic:
|
8
|
+
|
9
|
+
class Globals < Lomic
|
10
|
+
var :players => []
|
11
|
+
var :currentPlayer
|
12
|
+
end
|
13
|
+
|
14
|
+
class Player < Lomic
|
15
|
+
resource :hp => 15 # resources have a max and min value
|
16
|
+
end
|
17
|
+
|
18
|
+
rule 101 do |g| # g refers to globals
|
19
|
+
### The game begins with 4 players.
|
20
|
+
### Each player is assigned a unique number.
|
21
|
+
event "game:start" do
|
22
|
+
Player.new_var :number => 0
|
23
|
+
4.times do |i|
|
24
|
+
p = Player.new
|
25
|
+
p.number = i
|
26
|
+
g.players.push(p)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
rule 102 do |g|
|
32
|
+
### At the beginning of each player's turn,
|
33
|
+
### that player takes 3 damage
|
34
|
+
event "turn:start" do
|
35
|
+
currentPlayer.hp -= 3
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
== Getting Started
|
40
|
+
|
41
|
+
Download the source and run an example:
|
42
|
+
|
43
|
+
$ git clone git://github.com/mindeavor/Lomic.git
|
44
|
+
$ cd Lomic
|
45
|
+
$ ruby parse.rb examples/simple.rb
|
46
|
+
|
47
|
+
Check out the `examples/` folder to see what Lomic is supposed to look like, and `parse.rb` to see how to use Lomic (in its current, underdeveloped state)
|
48
|
+
|
49
|
+
== Contributing
|
50
|
+
|
51
|
+
Lomic is currently in the concept and development stage. To discuss contributing, syntax, goals, or implementation, join us at #lomic on irc.freenode, or email me at gilbertbgarza aT gmail
|
52
|
+
|
53
|
+
== Copyright
|
54
|
+
|
55
|
+
Copyright (c) 2010 Gilbert B Garza. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "lomic"
|
8
|
+
gem.summary = %Q{A Ruby DSL for the game Nomic}
|
9
|
+
gem.description = %Q{Lomic is a Domain Specific Language (DSL) intended to be used for Pomic, a programming version of the game Nomic.}
|
10
|
+
gem.email = "gilbertbgarza@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/mindeavor/lomic"
|
12
|
+
gem.authors = ["Gilbert B Garza"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
+
gem.add_development_dependency "cucumber", ">= 0.8.0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/test_*.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
begin
|
45
|
+
require 'cucumber/rake/task'
|
46
|
+
Cucumber::Rake::Task.new(:features)
|
47
|
+
|
48
|
+
task :features => :check_dependencies
|
49
|
+
rescue LoadError
|
50
|
+
task :features do
|
51
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
task :default => :test
|
56
|
+
|
57
|
+
require 'rake/rdoctask'
|
58
|
+
Rake::RDocTask.new do |rdoc|
|
59
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
60
|
+
|
61
|
+
rdoc.rdoc_dir = 'rdoc'
|
62
|
+
rdoc.title = "lomic #{version}"
|
63
|
+
rdoc.rdoc_files.include('README*')
|
64
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
65
|
+
end
|
data/TODO
ADDED
data/VERSION.yml
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
class Player < Lomic
|
2
|
+
var :number
|
3
|
+
end
|
4
|
+
|
5
|
+
# global data: all lomic functions search exclusively in this class for global variables
|
6
|
+
class Globals < Lomic
|
7
|
+
var :currentRule # Rule is a built-in class type
|
8
|
+
var :ruleChangeType => ""
|
9
|
+
|
10
|
+
var :players => []
|
11
|
+
var :currentPlayer
|
12
|
+
|
13
|
+
var :num => 99
|
14
|
+
|
15
|
+
var :turnCounter => 0, :ruleCounter => 301
|
16
|
+
|
17
|
+
var :playersVoted => Set.new
|
18
|
+
var :votesUnanimous => false # since this is a boolean, it's accessed with: votesUnanimous?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Rules
|
22
|
+
|
23
|
+
# Rules recieve events in the order of their priority *for that event* (standard is 5)
|
24
|
+
# If two rules have the same priority, the lowest rule number goes first
|
25
|
+
|
26
|
+
# A failed cond will cause the rule to ignore the event
|
27
|
+
# A failed assert will cause the rule to kill the event entirely and
|
28
|
+
# set the next event to "assert:fail" with failed_event containing
|
29
|
+
# the name of the failed event
|
30
|
+
|
31
|
+
# Both cond and assert failures will cause the rule to stop executing.
|
32
|
+
|
33
|
+
rule 102 do |g|
|
34
|
+
### Initially the rules in the 100's are immutable and rules in
|
35
|
+
### the 200's are mutable. Rules subsequently enacted or transmuted
|
36
|
+
### (that is, changed from immutable to mutable or vice versa) may be
|
37
|
+
### immutable or mutable regardless of their numbers, and rules in the
|
38
|
+
### Initial Set may be transmuted regardless of their numbers
|
39
|
+
event "game:start" do
|
40
|
+
# create a new accessible variable in the Rule class initialized to false
|
41
|
+
Rule.new_var :immutable => false
|
42
|
+
# rules is a reserved global array
|
43
|
+
g.rules.each do |r|
|
44
|
+
r.immutable = true if r.number >= 100 and r.number < 200
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
event "rule:change", :priority => 1 do
|
49
|
+
cond g.ruleChangeType == "transmute"
|
50
|
+
assert g.currentRule.immutable == false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
rule 103 do |g|
|
55
|
+
### A rule change is any of the following:
|
56
|
+
### (1) the enactment, repeal, or amendment of a mutable rule;
|
57
|
+
### (2) the enactment, repeal, or amendment of an amendment of a mutable rule;
|
58
|
+
### or (3) the transmutation of an immutable rule into a mutable rule or vice versa.
|
59
|
+
event "rule:change" do
|
60
|
+
assert g.ruleChangeType.isEither ["repeal","amendment","transmute"]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
rule 104 do |g|
|
65
|
+
### All rule-changes proposed in the proper way shall be voted on.
|
66
|
+
event "rule:change" do
|
67
|
+
set_next "players:vote"
|
68
|
+
end
|
69
|
+
|
70
|
+
### They will be adopted if and only if they receive the required number of votes.
|
71
|
+
event "rule:voted" do
|
72
|
+
set_next "vote:unanimous?"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
rule 105 do |g|
|
77
|
+
### Every player is an eligible voter.
|
78
|
+
event "game:start" do
|
79
|
+
Player.new_var 'voter' => true
|
80
|
+
Player.new_var 'votedYes' => false
|
81
|
+
end
|
82
|
+
|
83
|
+
### Every eligible voter must participate in every vote on rule-changes.
|
84
|
+
event "players:vote" do
|
85
|
+
# This will send JSON data over the network to the external manager
|
86
|
+
# that is managing this instance of Lomic
|
87
|
+
send "request:votes"
|
88
|
+
# This will listen for a message from the external manager and treat it an event
|
89
|
+
listen "player:vote"
|
90
|
+
end
|
91
|
+
|
92
|
+
event "player:vote" do
|
93
|
+
# start listening before the assertions in case one of them fails
|
94
|
+
# listening, like next_event, takes place after the code block is done executing
|
95
|
+
listen "player:vote"
|
96
|
+
|
97
|
+
assert g.currentPlayer.voter? == true
|
98
|
+
assert g.playersVoted.include? currentPlayer == false
|
99
|
+
playersVoted.add(currentPlayer)
|
100
|
+
|
101
|
+
allVoted = true
|
102
|
+
g.players.each do |p|
|
103
|
+
allVoted = false if p.voter? and g.playersVoted.include? p == false
|
104
|
+
end
|
105
|
+
|
106
|
+
if allVoted
|
107
|
+
listen :cancel # necessary because listening has higher priority over next_event
|
108
|
+
set_next "rule:voted"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
rule 108 do |g|
|
114
|
+
### Each proposed rule-change shall be given a number for reference.
|
115
|
+
### The numbers shall begin with 301, and each rule-change proposed
|
116
|
+
### in the proper way shall receive the next successive integer,
|
117
|
+
### whether or not the proposal is adopted.
|
118
|
+
event "rule:passed" do
|
119
|
+
# store global data
|
120
|
+
# modify source code for rule currentRule.number
|
121
|
+
# reload source code
|
122
|
+
# reload global data
|
123
|
+
# set_next "rule:complete" on load
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
rule 201 do |g|
|
128
|
+
### Players shall alternate in clockwise order, taking one whole turn apiece.
|
129
|
+
### Turns may not be skipped or passed, and parts of turns may not be omitted.
|
130
|
+
### All players begin with zero points.
|
131
|
+
event "game:start" do
|
132
|
+
Player.new_var 'points', 0
|
133
|
+
g.turnCounter = 0
|
134
|
+
g.currentPlayer = players[0]
|
135
|
+
set_next "turn:start"
|
136
|
+
end
|
137
|
+
|
138
|
+
event "turn:end" do
|
139
|
+
g.turnCounter += 1
|
140
|
+
g.currentPlayer = g.players[g.turnCounter % g.players.size]
|
141
|
+
set_next "turn:start"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
rule 202 do |g|
|
146
|
+
### One turn consists of two parts in this order:
|
147
|
+
### (1) proposing one rule-change and having it voted on, and
|
148
|
+
event "turn:start" do
|
149
|
+
# prompt external manager for action
|
150
|
+
send :event => "request:action", :player => g.currentPlayer.number
|
151
|
+
g.ruleChangeType = listen "response:action"
|
152
|
+
|
153
|
+
set_next "rule:change"
|
154
|
+
end
|
155
|
+
|
156
|
+
### (2) throwing one die once and adding the number of points on its face to one's score.
|
157
|
+
event "rule:complete" do
|
158
|
+
set_next "player:dice roll"
|
159
|
+
end
|
160
|
+
|
161
|
+
event "player:dice roll" do
|
162
|
+
g.currentPlayer.points += ran(6)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
rule 203 do |g|
|
167
|
+
### A rule-change is adopted if and only if the vote is unanimous among the eligible voters.
|
168
|
+
event "vote:unanimous?" do
|
169
|
+
g.votesUnanimous = true
|
170
|
+
g.votedPlayers.each { |p| g.votesUnanimous = false if p.votedYes? == false }
|
171
|
+
assert g.votesUnanimous?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
rule 208 do |g|
|
176
|
+
### The winner is the first player to achieve 100 (positive) points.
|
177
|
+
event "player:dice roll", :priority => 1 do
|
178
|
+
if g.currentPlayer.points >= 100
|
179
|
+
set_next "player:win by points"
|
180
|
+
else
|
181
|
+
set_next "turn:end"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Globals < Lomic
|
2
|
+
var :number => 100
|
3
|
+
end
|
4
|
+
|
5
|
+
rule 101 do |g| # g refers to globals
|
6
|
+
event "game:start" do
|
7
|
+
puts '[Example: priority.rb]'
|
8
|
+
g.number = 99
|
9
|
+
set_next "route:right"
|
10
|
+
end
|
11
|
+
|
12
|
+
# A lower priority number means it runs closer to the end.
|
13
|
+
# The default priority is 5, thus this event block
|
14
|
+
# runs last, having the last effect on set_next and g.number
|
15
|
+
event "game:start", :priority => 1 do
|
16
|
+
g.number = 77
|
17
|
+
set_next "route:left"
|
18
|
+
end
|
19
|
+
|
20
|
+
event "game:start" do
|
21
|
+
g.number = 55
|
22
|
+
set_next "route:right"
|
23
|
+
end
|
24
|
+
|
25
|
+
event "route:left" do
|
26
|
+
puts "The number is: #{g.number}"
|
27
|
+
puts "You went left!"
|
28
|
+
end
|
29
|
+
|
30
|
+
event "route:right" do
|
31
|
+
puts "The number is: #{g.number}"
|
32
|
+
puts "You went right!"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class Globals < Lomic
|
2
|
+
var :didiwin => 'No...'
|
3
|
+
end
|
4
|
+
|
5
|
+
rule 101 do |g| # g refers to globals
|
6
|
+
event "game:start" do
|
7
|
+
puts '[Example: simple.rb]'
|
8
|
+
g.didiwin = 'Yes!'
|
9
|
+
set_next "game:test1"
|
10
|
+
end
|
11
|
+
|
12
|
+
event "game:test1" do
|
13
|
+
puts "Did I win? #{g.didiwin}"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
Feature: resources in class declarations
|
2
|
+
In order to remove boiler plate code
|
3
|
+
Resources auto generate convenience methods
|
4
|
+
Geared towards game resources
|
5
|
+
Such as hit points and energy
|
6
|
+
|
7
|
+
Scenario: Declaring a resource should generate additional variables
|
8
|
+
Given the Lomic inherited class MyClass
|
9
|
+
And the resource declaration HP with value 17
|
10
|
+
When I create a new MyClass object named foo
|
11
|
+
Then the result of foo.HP should be 17
|
12
|
+
And the result of foo.HPmax should be 17
|
13
|
+
And the result of foo.HPmin should be 0
|
14
|
+
|
15
|
+
Scenario: A resources should automatically be kept within its defined limits
|
16
|
+
Given the Lomic inherited class MyClass
|
17
|
+
And the resource declaration HP with value 17
|
18
|
+
And the resource declaration MP with value 5
|
19
|
+
When I create a new MyClass object named foo
|
20
|
+
And I add 33 to foo.HP
|
21
|
+
And I subtract 99 from foo.MP
|
22
|
+
Then the result of foo.HP should be 17
|
23
|
+
And the result of foo.MP should be 0
|
24
|
+
|
25
|
+
Scenario: Resource limits can be explicitly defined
|
26
|
+
Given the Lomic inherited class MyClass
|
27
|
+
And the resource declaration EXP with value [0,15,0]
|
28
|
+
And the resource declaration STR with value [1,5]
|
29
|
+
And the resource declaration HP with value [17]
|
30
|
+
When I create a new MyClass object named foo
|
31
|
+
Then the results of each <var> should be <val>:
|
32
|
+
| var | val |
|
33
|
+
| foo.EXP | 0 |
|
34
|
+
| foo.EXPmin | 0 |
|
35
|
+
| foo.EXPmax | 15 |
|
36
|
+
| foo.STR | 5 |
|
37
|
+
| foo.STRmin | 1 |
|
38
|
+
| foo.STRmax | 5 |
|
39
|
+
| foo.HP | 17 |
|
40
|
+
| foo.HPmin | 0 |
|
41
|
+
| foo.HPmax | 17 |
|
@@ -0,0 +1,39 @@
|
|
1
|
+
Given /^the Lomic inherited class (\w*)/ do |klass|
|
2
|
+
eval "class #{klass} < Lomic\nend"
|
3
|
+
@class = Kernel.const_get(klass)
|
4
|
+
end
|
5
|
+
|
6
|
+
When /^I create a new (\w*) object named (\w*)/ do |klass,obj_name|
|
7
|
+
eval "$#{obj_name} = #{klass}.new"
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^the result of (\w+)\.(\w+) should be (\w+)/ do |obj_name,var_name,result|
|
11
|
+
result == (eval "$#{obj_name}.#{var_name}")
|
12
|
+
end
|
13
|
+
|
14
|
+
Then /^the results of each <var> should be <val>/ do |table|
|
15
|
+
@alltrue = true
|
16
|
+
table.hashes.each do |h|
|
17
|
+
@alltrue = @alltrue and h[:val] == (eval "$#{h[:var]}")
|
18
|
+
end
|
19
|
+
@alltrue
|
20
|
+
end
|
21
|
+
|
22
|
+
### general variables
|
23
|
+
|
24
|
+
When /^I (add|subtract) (\d+) (?:to|from) (.*)/ do |type,amt,var|
|
25
|
+
op = (type=='add') ? '+' : '-'
|
26
|
+
eval "$#{var} #{op}= #{amt}"
|
27
|
+
end
|
28
|
+
|
29
|
+
### var declaration
|
30
|
+
|
31
|
+
Given /^the following var declarations:/ do |vars|
|
32
|
+
vars.hashes.each { |h| @class.class_eval h[:var_decl] }
|
33
|
+
end
|
34
|
+
|
35
|
+
### resources
|
36
|
+
|
37
|
+
Given /^the resource declaration (\w+) with value (\[.*\]|\d+)/ do |name,val|
|
38
|
+
@class.class_eval "resource :#{name} => #{val}"
|
39
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: variable declaration and initialization on new objects
|
2
|
+
In order to keep variable declarations consistent
|
3
|
+
With other Lomic features
|
4
|
+
Such as resources
|
5
|
+
|
6
|
+
Scenario: Use of var after new object initializes
|
7
|
+
Given the Lomic inherited class MyClass
|
8
|
+
And the following var declarations:
|
9
|
+
| var_decl |
|
10
|
+
| var :example => 9 |
|
11
|
+
| var :another => 3 |
|
12
|
+
When I create a new MyClass object named foo
|
13
|
+
Then the result of foo.example should be 9
|
14
|
+
And the result of foo.another should be 3
|
data/lib/Lomic.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$:.push File.expand_path(File.dirname(__FILE__) + '/lib')
|
2
|
+
require 'lomic/Event'
|
3
|
+
require 'lomic/EventEngine'
|
4
|
+
require 'lomic/GameState'
|
5
|
+
require 'lomic/Lomic'
|
6
|
+
require 'lomic/Rule'
|
7
|
+
require 'lomic/LomicParser'
|
8
|
+
|
9
|
+
module Lomic
|
10
|
+
VERSION = '0.0.1'
|
11
|
+
|
12
|
+
def self.parse(filename, start_event="game:start")
|
13
|
+
gstate = LomicParser.load_source(filename)
|
14
|
+
gstate.emit start_event
|
15
|
+
end
|
16
|
+
end
|
data/lib/lomic/Event.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module Lomic
|
2
|
+
|
3
|
+
class EventEngine
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@stack = []
|
7
|
+
@counter = 0 # the number of event code blocks executed
|
8
|
+
end
|
9
|
+
|
10
|
+
def run(event_name, rules)
|
11
|
+
add_sort_events rules
|
12
|
+
@next_event = event_name
|
13
|
+
|
14
|
+
begin
|
15
|
+
event_name = @next_event
|
16
|
+
@next_event = nil
|
17
|
+
for event in @events[event_name]
|
18
|
+
# event code blocks can set @next_event through set_next
|
19
|
+
instance_eval &event.block
|
20
|
+
end
|
21
|
+
end while @next_event.nil? == false
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_next(event_name)
|
25
|
+
@next_event = event_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def next_event
|
29
|
+
@next_event
|
30
|
+
end
|
31
|
+
|
32
|
+
def counter
|
33
|
+
@counter
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def add_sort_events(rules)
|
39
|
+
# "event_name" => [Event]
|
40
|
+
@events = {}
|
41
|
+
rules.each do |r|
|
42
|
+
r.event_bag.each do |name, event_arr|
|
43
|
+
for e in event_arr do
|
44
|
+
if @events[name].nil?
|
45
|
+
@events[name] = [e]
|
46
|
+
next
|
47
|
+
end
|
48
|
+
# insert into sorted spot
|
49
|
+
arr = @events[name]
|
50
|
+
i = 0
|
51
|
+
arr.each do |arr_e|
|
52
|
+
if e.priority > arr_e.priority
|
53
|
+
arr.insert(i,e)
|
54
|
+
break
|
55
|
+
elsif i == arr.size-1
|
56
|
+
arr.insert(i+1,e)
|
57
|
+
break
|
58
|
+
end
|
59
|
+
i += 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def push(event)
|
67
|
+
# pushes a state onto the stack
|
68
|
+
state = {
|
69
|
+
:event_name => event.name,
|
70
|
+
:priority => event.priority,
|
71
|
+
:rule_number => event.rule_number
|
72
|
+
}
|
73
|
+
@stack.push(state)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end # module
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Lomic
|
2
|
+
|
3
|
+
require 'Set'
|
4
|
+
|
5
|
+
class GameState
|
6
|
+
def initialize
|
7
|
+
super
|
8
|
+
@globals
|
9
|
+
@rules = []
|
10
|
+
@em = EventEngine.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def globals
|
14
|
+
@globals
|
15
|
+
end
|
16
|
+
|
17
|
+
def globals=(globals_obj)
|
18
|
+
return if @globals.nil? == false
|
19
|
+
@globals = globals_obj
|
20
|
+
klass = @globals.class
|
21
|
+
|
22
|
+
klass.new_var :rules => []
|
23
|
+
@globals.rules = @rules
|
24
|
+
end
|
25
|
+
|
26
|
+
def addRule(rule)
|
27
|
+
@rules.push(rule)
|
28
|
+
end
|
29
|
+
|
30
|
+
def emit(event_name)
|
31
|
+
@em.run(event_name,@globals.rules)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end # module
|
data/lib/lomic/Lomic.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
module Lomic
|
2
|
+
|
3
|
+
### This class helps clean up in-game class definitions
|
4
|
+
class Lomic
|
5
|
+
class << self
|
6
|
+
public :define_method, :remove_method
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.var(symbols)
|
10
|
+
if symbols.instance_of? Symbol
|
11
|
+
self.new_var({symbols.to_sym => nil})
|
12
|
+
else
|
13
|
+
symbols.each { |name,init_val|
|
14
|
+
self.new_var(name => init_val)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.new_var(symbol)
|
20
|
+
name = symbol.keys[0]
|
21
|
+
init_val = symbol[name]
|
22
|
+
|
23
|
+
self.define_method name do
|
24
|
+
getter = "@#{name}"
|
25
|
+
getter += '?' if init_val.instance_of? TrueClass or init_val.instance_of? FalseClass
|
26
|
+
val = instance_variable_get getter
|
27
|
+
|
28
|
+
if val.nil? and @init_used[name].nil?
|
29
|
+
inits = self.class.class_eval "@@__#{className}_inits__"
|
30
|
+
val = inits[name]
|
31
|
+
instance_variable_set("@#{name}", val)
|
32
|
+
@init_used[name] = true
|
33
|
+
end
|
34
|
+
|
35
|
+
return val
|
36
|
+
end
|
37
|
+
|
38
|
+
self.define_method "#{name}=" do |new_val|
|
39
|
+
instance_variable_set("@#{name}", new_val)
|
40
|
+
@init_used[name] = true
|
41
|
+
end
|
42
|
+
|
43
|
+
class_eval "@@__#{self.className}_inits__ ||= {}"
|
44
|
+
inits = class_eval "@@__#{self.className}_inits__"
|
45
|
+
inits[name] = init_val
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.resource(symbols)
|
49
|
+
symbols.each { |name,init_val|
|
50
|
+
self.new_resource(name => init_val)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.new_resource(symbol)
|
55
|
+
name = symbol.keys[0]
|
56
|
+
init_val = symbol[name]
|
57
|
+
|
58
|
+
if init_val.instance_of? Array
|
59
|
+
min,max,init = case init_val.size
|
60
|
+
when 1 then [0,init_val[0],init_val[0]]
|
61
|
+
when 2 then [init_val[0],init_val[1],init_val[1]]
|
62
|
+
else [init_val[0],init_val[1],init_val[2]]
|
63
|
+
end
|
64
|
+
else
|
65
|
+
min,max,init = [0,init_val,init_val]
|
66
|
+
end
|
67
|
+
self.new_var(name => init)
|
68
|
+
self.new_var("#{name}min" => min)
|
69
|
+
self.new_var("#{name}max" => max)
|
70
|
+
|
71
|
+
# redefine the set method to enforce resource limits
|
72
|
+
self.remove_method "#{name}="
|
73
|
+
self.define_method "#{name}=" do |new_val|
|
74
|
+
min = instance_variable_get "@#{name}min"
|
75
|
+
max = instance_variable_get "@#{name}max"
|
76
|
+
new_val = new_val < min ? min : (new_val > max ? max : new_val)
|
77
|
+
|
78
|
+
instance_variable_set("@#{name}", new_val)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize
|
83
|
+
@init_used = {}
|
84
|
+
inits = self.class.class_eval "@@__#{className}_inits__"
|
85
|
+
inits.each do |var,val|
|
86
|
+
instance_variable_set "@#{var}", val
|
87
|
+
@init_used[var] = true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.className
|
92
|
+
if self.name.include? '::'
|
93
|
+
self.name.split('::')[1]
|
94
|
+
else
|
95
|
+
self.name
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def className
|
100
|
+
self.class.className
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end # module
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Lomic
|
2
|
+
|
3
|
+
class LomicParser
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
# points to the Rule that is currently being parsed
|
7
|
+
@currentRule = nil
|
8
|
+
# The entire state of the game
|
9
|
+
@state = GameState.new
|
10
|
+
@first_rule = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def rule(number)
|
14
|
+
# TODO: ensure number is int
|
15
|
+
@currentRule = Rule.new(number)
|
16
|
+
if @first_rule
|
17
|
+
@state.globals = instance_eval 'Globals.new'
|
18
|
+
@first_rule = false
|
19
|
+
end
|
20
|
+
|
21
|
+
yield @state.globals
|
22
|
+
ensure
|
23
|
+
@state.addRule(@currentRule)
|
24
|
+
@currentRule = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def event(event_name, options={}, &block)
|
28
|
+
@currentRule.event(event_name, options, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def gamestate
|
32
|
+
@state
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.load_source(filename)
|
36
|
+
dsl = new
|
37
|
+
dsl.instance_eval(File.read(filename),filename)
|
38
|
+
# dsl.gamestate.globals = (dsl.instance_eval 'Globals.new')
|
39
|
+
dsl.gamestate
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end # module
|
data/lib/lomic/Rule.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Lomic
|
2
|
+
|
3
|
+
class Rule < Lomic
|
4
|
+
def initialize(number)
|
5
|
+
@number = number
|
6
|
+
@event_bag = {} # "event_name" => [Event]
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def event(event_name, options={}, &block)
|
11
|
+
options[:priority] ||= 5
|
12
|
+
options[:name] = event_name
|
13
|
+
options[:block] = block
|
14
|
+
|
15
|
+
event = Event.new(options)
|
16
|
+
if @event_bag[event_name].nil?
|
17
|
+
@event_bag[event_name] = [event]
|
18
|
+
else
|
19
|
+
# insert into sorted spot
|
20
|
+
arr = @event_bag[event_name]
|
21
|
+
i = 0
|
22
|
+
arr.each do |e|
|
23
|
+
if event.priority > e.priority
|
24
|
+
arr.insert(i,event)
|
25
|
+
break
|
26
|
+
elsif i == arr.size-1
|
27
|
+
arr.insert(i+1,event)
|
28
|
+
break
|
29
|
+
end
|
30
|
+
i += 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def event_bag
|
36
|
+
@event_bag
|
37
|
+
end
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
"Rule #{@number}: #{@event_bag.inspect}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end # module
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lomic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Gilbert B Garza
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-12 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: thoughtbot-shoulda
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: cucumber
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 63
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
- 8
|
47
|
+
- 0
|
48
|
+
version: 0.8.0
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
description: Lomic is a Domain Specific Language (DSL) intended to be used for Pomic, a programming version of the game Nomic.
|
52
|
+
email: gilbertbgarza@gmail.com
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files:
|
58
|
+
- LICENSE
|
59
|
+
- README.rdoc
|
60
|
+
- TODO
|
61
|
+
files:
|
62
|
+
- LICENSE
|
63
|
+
- Manifest
|
64
|
+
- README.rdoc
|
65
|
+
- Rakefile
|
66
|
+
- TODO
|
67
|
+
- VERSION.yml
|
68
|
+
- examples/nomic_initial_rules.rb
|
69
|
+
- examples/priority.rb
|
70
|
+
- examples/simple.rb
|
71
|
+
- features/resource.feature
|
72
|
+
- features/step_definitions/steps.rb
|
73
|
+
- features/support/env.rb
|
74
|
+
- features/var_init.feature
|
75
|
+
- lib/Lomic.rb
|
76
|
+
- lib/lomic/Event.rb
|
77
|
+
- lib/lomic/EventEngine.rb
|
78
|
+
- lib/lomic/GameState.rb
|
79
|
+
- lib/lomic/Lomic.rb
|
80
|
+
- lib/lomic/LomicParser.rb
|
81
|
+
- lib/lomic/Rule.rb
|
82
|
+
has_rdoc: true
|
83
|
+
homepage: http://github.com/mindeavor/lomic
|
84
|
+
licenses: []
|
85
|
+
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options:
|
88
|
+
- --charset=UTF-8
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
hash: 3
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
requirements: []
|
110
|
+
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 1.3.7
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: A Ruby DSL for the game Nomic
|
116
|
+
test_files:
|
117
|
+
- examples/nomic_initial_rules.rb
|
118
|
+
- examples/priority.rb
|
119
|
+
- examples/simple.rb
|