minigame 0.0.1 → 0.0.2
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.
- checksums.yaml +8 -8
- data/.gitignore +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +11 -9
- data/README.md +7 -1
- data/Rakefile +1 -0
- data/lib/minigame/gameable.rb +4 -1
- data/lib/minigame/version.rb +1 -1
- data/minigame.gemspec +4 -3
- data/spec/models/array_of_hashes_spec.rb +94 -0
- data/spec/models/gameable_spec.rb +4 -0
- data/spec/models/games_spec.rb +280 -0
- data/spec/models/player_spec.rb +18 -0
- data/spec/models/strategy_profile_spec.rb +29 -0
- data/spec/models/strategy_spec.rb +19 -0
- data/spec/spec_helper.rb +55 -0
- data/test/test_minigame.rb +3 -3
- metadata +34 -6
- data/lib/minigame/payoff.rb +0 -12
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OWQ1MjQwNjNjNGM5MTZiNWQ2ZDE3ZjQ4YWUxYmZmZjMyM2QxY2NmNA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZjMyNjg1N2ZjYjY0ODI1YmY5ZTdhMDA1NzkxM2Y5OWRhOGIxMThhMA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YzBlNDY5ODdkZTdlMTRjNzExYTNhMjVlZGViYzQxMDZlNzdkNDMyODQyNzhk
|
10
|
+
Y2RiMzA0NGU4OGM1M2IxMzY5YmM5ZGQ1MTJhNDQyMTYyMzJiNjUwYzYxYzVi
|
11
|
+
NzljNDg0NzQ1OTg5ZjAxNzhjZDc2MTg1NGRlZWYyODI1Y2E4MjQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YjcyMmE3MjUyYzg0MDM5NGQyZWNmYmY4NDdjNjllYzdmZmI4YTYwNWYxZmM0
|
14
|
+
ODE4Mjk2Y2YwMTg5MmRiNzEzM2YzMjgyNGUzZjM1NzdlMDFhZjRmMWMzZjNm
|
15
|
+
YmYyMDM4NDY1MjZjNjRhNjYxMDJiY2ZjNWJlMGE4MDk0ZmFiMjM=
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,27 +1,29 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
minigame (0.0.
|
4
|
+
minigame (0.0.2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
|
10
|
-
debugger (1.6.1)
|
11
|
-
columnize (>= 0.3.1)
|
12
|
-
debugger-linecache (~> 1.2.0)
|
13
|
-
debugger-ruby_core_source (~> 1.2.3)
|
14
|
-
debugger-linecache (1.2.0)
|
15
|
-
debugger-ruby_core_source (1.2.3)
|
9
|
+
diff-lcs (1.2.4)
|
16
10
|
minitest (5.0.6)
|
17
11
|
rake (10.1.0)
|
12
|
+
rspec (2.13.0)
|
13
|
+
rspec-core (~> 2.13.0)
|
14
|
+
rspec-expectations (~> 2.13.0)
|
15
|
+
rspec-mocks (~> 2.13.0)
|
16
|
+
rspec-core (2.13.1)
|
17
|
+
rspec-expectations (2.13.0)
|
18
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
19
|
+
rspec-mocks (2.13.1)
|
18
20
|
|
19
21
|
PLATFORMS
|
20
22
|
ruby
|
21
23
|
|
22
24
|
DEPENDENCIES
|
23
25
|
bundler (~> 1.3)
|
24
|
-
debugger
|
25
26
|
minigame!
|
26
27
|
minitest
|
27
28
|
rake
|
29
|
+
rspec
|
data/README.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
[](https://travis-ci.org/wavell/minigame)
|
1
2
|
# MiniGame
|
2
3
|
## Simplistic Game Theory Library in Ruby
|
3
4
|
|
5
|
+
Installation
|
6
|
+
```
|
7
|
+
gem install minigame
|
8
|
+
```
|
9
|
+
|
4
10
|
Usage:
|
5
11
|
|
6
12
|
To create new players:
|
@@ -70,7 +76,7 @@ To get the list a weakly dominated strategies:
|
|
70
76
|
# ------------------------|---------------------|------------------
|
71
77
|
gs.weakly_dominated_list
|
72
78
|
|
73
|
-
[{:name=>"Generic Training"}
|
79
|
+
[{:name=>"Generic Training"}]
|
74
80
|
|
75
81
|
```
|
76
82
|
|
data/Rakefile
CHANGED
data/lib/minigame/gameable.rb
CHANGED
@@ -79,7 +79,10 @@ module Gameable
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
82
|
-
dominated_list.flatten.uniq
|
82
|
+
dom = dominated_list.flatten.uniq
|
83
|
+
strict = strictly_dominated_list
|
84
|
+
weakly_dominated_list = dom.take_while{|i| strict.include?(i)==false}
|
85
|
+
weakly_dominated_list
|
83
86
|
end
|
84
87
|
|
85
88
|
def strategies_by_player(player)
|
data/lib/minigame/version.rb
CHANGED
data/minigame.gemspec
CHANGED
@@ -6,11 +6,11 @@ require 'minigame/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "minigame"
|
8
8
|
spec.version = Minigame::VERSION
|
9
|
-
spec.authors = ["
|
10
|
-
spec.email = ["
|
9
|
+
spec.authors = ["W Watson"]
|
10
|
+
spec.email = ["me@wwatson.me"]
|
11
11
|
spec.description = %q{A game theory library}
|
12
12
|
spec.summary = %q{A minimalistic game theory library that computes nash equilibrium}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/wavell/minigame"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -21,5 +21,6 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "minitest"
|
24
|
+
spec.add_development_dependency "rspec"
|
24
25
|
|
25
26
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ArrayOfHashes do
|
4
|
+
before(:each) do
|
5
|
+
class TestAOfH
|
6
|
+
include ArrayOfHashes
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should instantiate with an array of hashes" do
|
11
|
+
aoh = TestAOfH.new [{keyname1: "test1", keyname2: "test2"},{keyname3: "test3", keyname4: "test4"}]
|
12
|
+
aoh.count.should == 2
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have enumerable functions" do
|
16
|
+
aoh = TestAOfH.new [{keyname1: "test1", keyname2: "test2"},{keyname3: "test3", keyname4: "test4"}]
|
17
|
+
@test2 = aoh.select{|x| x[:keyname2] == "test2"}
|
18
|
+
@test2.count.should == 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it ".required_keys should reject non-symbols" do
|
22
|
+
aoh=TestAOfH.new
|
23
|
+
expect {
|
24
|
+
aoh.required_keys ["name", "description"]
|
25
|
+
}.to raise_error(RuntimeError,/Not a symbol/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it ".required_keys should work as assignment" do
|
29
|
+
aoh=TestAOfH.new
|
30
|
+
expect {
|
31
|
+
aoh.required_keys = [:name, :description]
|
32
|
+
}.to_not raise_error
|
33
|
+
end
|
34
|
+
|
35
|
+
it ".required_keys should also work with a single symbol (not an array)" do
|
36
|
+
aoh=TestAOfH.new
|
37
|
+
expect {
|
38
|
+
aoh.required_keys = :name
|
39
|
+
}.to_not raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it ".required_keys should accept a list of symbols" do
|
43
|
+
aoh=TestAOfH.new
|
44
|
+
expect {
|
45
|
+
aoh.required_keys([:name, :description])
|
46
|
+
}.to_not raise_error
|
47
|
+
end
|
48
|
+
|
49
|
+
it ".check_keys should allow a name" do
|
50
|
+
aoh=TestAOfH.new
|
51
|
+
expect {
|
52
|
+
aoh.check_keys({name: "Deny Training"})
|
53
|
+
}.to_not raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
it ".check_keys should raise an error if not passed a hash" do
|
57
|
+
aoh=TestAOfH.new
|
58
|
+
expect {
|
59
|
+
aoh.check_keys('notahash')
|
60
|
+
}.to raise_error(RuntimeError,/Not a Hash/)
|
61
|
+
end
|
62
|
+
|
63
|
+
it ".check_keys should check for name" do
|
64
|
+
aoh=TestAOfH.new
|
65
|
+
aoh.required_keys :name
|
66
|
+
expect {
|
67
|
+
aoh.check_keys({hmm: 'notahash'})
|
68
|
+
}.to raise_error(RuntimeError,/Name required/)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should allow addition of hashes with <<" do
|
72
|
+
aoh = TestAOfH.new
|
73
|
+
aoh << {keyname1: "test1", keyname2: "test2"}
|
74
|
+
aoh << {keyname3: "test3", keyname4: "test4"}
|
75
|
+
aoh.count.should == 2
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should check for an required keys when initializing" do
|
79
|
+
aoh = TestAOfH.new
|
80
|
+
aoh.required_keys = [:name, :description]
|
81
|
+
expect {
|
82
|
+
aoh << {notname: "test1", description: "test2"}
|
83
|
+
}.to raise_error(RuntimeError,/Name required/)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should allow inserts when required keys are present" do
|
87
|
+
aoh = TestAOfH.new
|
88
|
+
aoh.required_keys = [:name, :description]
|
89
|
+
expect {
|
90
|
+
aoh << {name: "test1", description: "test2"}
|
91
|
+
aoh << {name: "test3", description: "test4"}
|
92
|
+
}.to_not raise_error
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,280 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Game do
|
4
|
+
|
5
|
+
# training game -- generic weakly dominated (not realistic)
|
6
|
+
#
|
7
|
+
# Employer: | Task Specific | Generic
|
8
|
+
# You: -----------------|---------------------|------------------
|
9
|
+
# | |
|
10
|
+
# Deny Training | (-2, -1) | (-2, -1)
|
11
|
+
# ------------------------|---------------------|------------------
|
12
|
+
# Accept Training | (1, 4) | (5, -2)
|
13
|
+
# ------------------------|---------------------|------------------
|
14
|
+
def game_with_weakly_dominated_strategies
|
15
|
+
strategies = Strategy.new [{name: "Accept Training"}, {name: "Generic Training"},{name: "Task Specific Training"},{name: "Deny Training"}]
|
16
|
+
accept = strategies.find{|x| x[:name] == "Accept Training"}
|
17
|
+
generic = strategies.find{|x| x[:name] == "Generic Training"}
|
18
|
+
task = strategies.find{|x| x[:name] == "Task Specific Training"}
|
19
|
+
deny = strategies.find{|x| x[:name] == "Deny Training"}
|
20
|
+
employer = Player.new name: "IBM"
|
21
|
+
employee = Player.new name: "John Smith"
|
22
|
+
|
23
|
+
strategy_profile = StrategyProfile.new
|
24
|
+
strategy_profile << {id: 1, strategy: generic, payoff: -2, player: employer}
|
25
|
+
strategy_profile << {id: 1, strategy: accept, payoff: 5, player: employee}
|
26
|
+
|
27
|
+
strategy_profile << {id: 2, strategy: generic, payoff: -1, player: employer}
|
28
|
+
strategy_profile << {id: 2, strategy: deny, payoff: -2, player: employee}
|
29
|
+
|
30
|
+
# not realistic
|
31
|
+
strategy_profile << {id: 3, strategy: task, payoff: -1, player: employer}
|
32
|
+
strategy_profile << {id: 3, strategy: deny, payoff: -2, player: employee}
|
33
|
+
|
34
|
+
strategy_profile << {id: 4, strategy: task, payoff: 4, player: employer}
|
35
|
+
strategy_profile << {id: 4, strategy: accept, payoff: 1, player: employee}
|
36
|
+
gs = Game.new
|
37
|
+
gs.strategy_profiles = strategy_profile
|
38
|
+
gs
|
39
|
+
end
|
40
|
+
|
41
|
+
# Regular training game -- generic strictly dominated
|
42
|
+
#
|
43
|
+
# Employer: | Task Specific | Generic
|
44
|
+
# You: -----------------|---------------------|------------------
|
45
|
+
# | |
|
46
|
+
# Deny Training | (-2, 0) | (-2, -1)
|
47
|
+
# ------------------------|---------------------|------------------
|
48
|
+
# Accept Training | (1, 4) | (5, -2)
|
49
|
+
# ------------------------|---------------------|------------------
|
50
|
+
before(:each) do
|
51
|
+
@strategies = Strategy.new [{name: "Accept Training"}, {name: "Generic Training"},{name: "Task Specific Training"},{name: "Deny Training"}]
|
52
|
+
@accept = @strategies.find{|x| x[:name] == "Accept Training"}
|
53
|
+
@generic = @strategies.find{|x| x[:name] == "Generic Training"}
|
54
|
+
@task = @strategies.find{|x| x[:name] == "Task Specific Training"}
|
55
|
+
@deny = @strategies.find{|x| x[:name] == "Deny Training"}
|
56
|
+
@employer = Player.new name: "IBM"
|
57
|
+
@employee = Player.new name: "John Smith"
|
58
|
+
|
59
|
+
@strategy_profile = StrategyProfile.new
|
60
|
+
@strategy_profile << {id: 1, strategy: @generic, payoff: -2, player: @employer}
|
61
|
+
@strategy_profile << {id: 1, strategy: @accept, payoff: 5, player: @employee}
|
62
|
+
|
63
|
+
@strategy_profile << {id: 2, strategy: @generic, payoff: -1, player: @employer}
|
64
|
+
@strategy_profile << {id: 2, strategy: @deny, payoff: -2, player: @employee}
|
65
|
+
|
66
|
+
@strategy_profile << {id: 3, strategy: @task, payoff: 0, player: @employer}
|
67
|
+
@strategy_profile << {id: 3, strategy: @deny, payoff: -2, player: @employee}
|
68
|
+
|
69
|
+
@strategy_profile << {id: 4, strategy: @task, payoff: 4, player: @employer}
|
70
|
+
@strategy_profile << {id: 4, strategy: @accept, payoff: 1, player: @employee}
|
71
|
+
@gs = Game.new
|
72
|
+
@gs.strategy_profiles = @strategy_profile
|
73
|
+
end
|
74
|
+
|
75
|
+
it ".strategies should allow assignment of strategies" do
|
76
|
+
@gs.strategy_profiles.count.should == 8
|
77
|
+
end
|
78
|
+
|
79
|
+
it ".payoff should return the payoff for a strategy and player" do
|
80
|
+
@gs.payoff(@generic, @employer).should == -2
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
it '.players should return a list of players' do
|
85
|
+
@gs.players.count.should == 2
|
86
|
+
end
|
87
|
+
|
88
|
+
it '.player_names should return a list of player names' do
|
89
|
+
@gs.player_names.count.should == 2
|
90
|
+
end
|
91
|
+
|
92
|
+
it '.opposing_players should return a list of players who receive payouts for the passed in strategy profile' do
|
93
|
+
gen_strat_profile = @gs.strategy_profiles.select{|x| x[:strategy][:name] == "Generic Training"}.first
|
94
|
+
@gs.opposing_players(gen_strat_profile).count.should == 1
|
95
|
+
end
|
96
|
+
|
97
|
+
it '.player_strategy_profiles should return a list of strategy profiles for the players' do
|
98
|
+
@gs.player_strategy_profiles(@employer).count.should == 4
|
99
|
+
end
|
100
|
+
|
101
|
+
it '.complementary_moves? should return a list of strategy profiles for a player other than the passed in profile' do
|
102
|
+
gen_strat_profile = @gs.strategy_profiles.select{|x| x[:strategy][:name] == @generic[:name]}.first
|
103
|
+
@gs.complementary_moves(gen_strat_profile).count.should == 1
|
104
|
+
@gs.complementary_moves(gen_strat_profile).first[:strategy][:name].should == @task[:name]
|
105
|
+
end
|
106
|
+
|
107
|
+
it '.opposing_player_strategy_profile should return the name of the opposing players strategy for the passed in profile' do
|
108
|
+
gen_strat_profile = @gs.strategy_profiles.select{|x| x[:strategy][:name] == @generic[:name]}.first
|
109
|
+
@gs.opposing_player_strategy_profile(gen_strat_profile)[:strategy][:name].should == @accept[:name]
|
110
|
+
end
|
111
|
+
|
112
|
+
it '.better_payoffs? should return a list of strategy profiles that are better than the passed in profile for the passed in player' do
|
113
|
+
gen_strat_profile = @gs.strategy_profiles.select{|x| x[:strategy][:name] == @generic[:name]}.first
|
114
|
+
#@gs.better_moves?(gen_strat_profile).first[:strategy][:name].should == @task[:name]
|
115
|
+
@gs.better_payoffs?(gen_strat_profile).first[:strategy][:name].should == @task[:name]
|
116
|
+
end
|
117
|
+
|
118
|
+
# should return this
|
119
|
+
# Employer: | Task Specific | Generic
|
120
|
+
# You: -----------------|---------------------|------------------
|
121
|
+
# | get all alternates | Pass in this
|
122
|
+
# Deny Training | ( , 0) | (-2, )
|
123
|
+
# ------------------------|---------------------|------------------
|
124
|
+
# Accept Training | | (5, )
|
125
|
+
# ------------------------|---------------------|------------------
|
126
|
+
it '.worse_payoffs? should return a list of strategy profiles that are worse than the passed in profile for the passed in player'do
|
127
|
+
gen_strat_profile = @gs.strategy_profiles.select{|x| x[:strategy][:name] == @accept[:name]}.first
|
128
|
+
@gs.worse_payoffs?(gen_strat_profile).should_not be_nil
|
129
|
+
@gs.worse_payoffs?(gen_strat_profile).should_not be_empty
|
130
|
+
@gs.worse_payoffs?(gen_strat_profile)[0][:payoff].should == -2
|
131
|
+
end
|
132
|
+
|
133
|
+
# Employer: | Task Specific | Generic
|
134
|
+
# You: -----------------|---------------------|------------------
|
135
|
+
# | |
|
136
|
+
# Deny Training | (-2, -1) | (-2, -1)
|
137
|
+
# ------------------------|---------------------|------------------
|
138
|
+
# Accept Training | (1, 4) | (5, -2)
|
139
|
+
# ------------------------|---------------------|------------------
|
140
|
+
it '.better_or_equal_moves? should return a list of strategy profiles that are better than the passed in profile for the passed in player' do
|
141
|
+
gs = game_with_weakly_dominated_strategies
|
142
|
+
# gets the generic strategy vs the deny training profile
|
143
|
+
gen_strat_profile = gs.strategy_profiles.select{|x| x[:strategy][:name] == @generic[:name]}[1]
|
144
|
+
gs.better_payoffs?(gen_strat_profile).count.should == 0
|
145
|
+
gs.better_or_equal_payoffs?(gen_strat_profile).count.should == 1
|
146
|
+
gs.better_or_equal_payoffs?(gen_strat_profile).first[:strategy][:name].should == @task[:name]
|
147
|
+
end
|
148
|
+
|
149
|
+
it ".strictly_dominated_list should return a list of strictly dominated strategies" do
|
150
|
+
# generic training and deny training are strictly dominated
|
151
|
+
@gs.strictly_dominated_list.count.should == 2
|
152
|
+
@gs.strictly_dominated_list.select{|x| x[:name] == @generic[:name]}.count.should > 0
|
153
|
+
@gs.strictly_dominated_list.select{|x| x[:name] == @deny[:name]}.count.should > 0
|
154
|
+
end
|
155
|
+
|
156
|
+
# Employer: | Task Specific | Generic
|
157
|
+
# You: -----------------|---------------------|------------------
|
158
|
+
# | |
|
159
|
+
# Deny Training | (-2, -1) | (-2, -1)
|
160
|
+
# ------------------------|---------------------|------------------
|
161
|
+
# Accept Training | (1, 4) | (5, -2)
|
162
|
+
# ------------------------|---------------------|------------------
|
163
|
+
it ".equal_moves should return a list of moves for a matchup that are equal in payoffs" do
|
164
|
+
# in the above game generic training is weakly dominated
|
165
|
+
gs = game_with_weakly_dominated_strategies
|
166
|
+
gen_strat_profiles = gs.strategy_profiles.select do |x|
|
167
|
+
x[:strategy][:name] == @generic[:name] &&
|
168
|
+
x[:payoff] == -1
|
169
|
+
end
|
170
|
+
gen_strat_profile = gen_strat_profiles.first
|
171
|
+
gs.equal_moves(gen_strat_profile).count.should == 1
|
172
|
+
gs.equal_moves(gen_strat_profile).first[:payoff].should == -1
|
173
|
+
end
|
174
|
+
|
175
|
+
# Employer: | Task Specific | Generic
|
176
|
+
# You: -----------------|---------------------|------------------
|
177
|
+
# | |
|
178
|
+
# Deny Training | (-2, -1) | (-2, -1)
|
179
|
+
# ------------------------|---------------------|------------------
|
180
|
+
# Accept Training | (1, 4) | (5, -2)
|
181
|
+
# ------------------------|---------------------|------------------
|
182
|
+
it ".all_other_moves should return a list of moves for a matchup that are equal in payoffs" do
|
183
|
+
# in the above game generic training is weakly dominated
|
184
|
+
gs = game_with_weakly_dominated_strategies
|
185
|
+
gen_strat_profiles = gs.strategy_profiles.select do |x|
|
186
|
+
x[:strategy][:name] == @generic[:name] &&
|
187
|
+
x[:payoff] == -1
|
188
|
+
end
|
189
|
+
gen_strat_profile = gen_strat_profiles.first
|
190
|
+
gs.equal_moves(gen_strat_profile).count.should == 1
|
191
|
+
gs.equal_moves(gen_strat_profile).first[:payoff].should == -1
|
192
|
+
end
|
193
|
+
|
194
|
+
# Employer: | Task Specific | Generic
|
195
|
+
# You: -----------------|---------------------|------------------
|
196
|
+
# | |
|
197
|
+
# Deny Training | (-2, -1) | (-2, -1)
|
198
|
+
# ------------------------|---------------------|------------------
|
199
|
+
# Accept Training | (1, 4) | (5, -2)
|
200
|
+
# ------------------------|---------------------|------------------
|
201
|
+
it ".weakly_dominated_list should return a list of weakly dominated strategies" do
|
202
|
+
# in the above game generic training is weakly dominated
|
203
|
+
gs = game_with_weakly_dominated_strategies
|
204
|
+
# generic should not be strictly but rather weakly dominated
|
205
|
+
gs.strictly_dominated_list.count.should == 1
|
206
|
+
gs.weakly_dominated_list.count.should == 1
|
207
|
+
gs.weakly_dominated_list.select{|x| x[:name] == @generic[:name]}.count.should > 0
|
208
|
+
end
|
209
|
+
it ".weakly_dominated_list should not return ant strictly dominated strategies" do
|
210
|
+
# in the above game generic training is weakly dominated
|
211
|
+
gs = game_with_weakly_dominated_strategies
|
212
|
+
# generic should not be strictly but rather weakly dominated
|
213
|
+
gs.strictly_dominated_list.count.should == 1
|
214
|
+
gs.weakly_dominated_list.count.should == 1
|
215
|
+
gs.weakly_dominated_list.select{|x| x[:name] == @generic[:name]}.count.should > 0
|
216
|
+
gs.weakly_dominated_list.select{|x| x[:name] == @deny[:name]}.count.should == 0
|
217
|
+
end
|
218
|
+
|
219
|
+
# Employer: | Task Specific | Generic
|
220
|
+
# You: -----------------|---------------------|------------------
|
221
|
+
# | |
|
222
|
+
# Deny Training | (-2, -1) | (-2, -1)
|
223
|
+
# ------------------------|---------------------|------------------
|
224
|
+
# Accept Training | (1, 4) | (5, -2)
|
225
|
+
# ------------------------|---------------------|------------------
|
226
|
+
it ".valid_deviation? should not be nil if the strategy and the deviation belongs to the same player" do
|
227
|
+
gs = game_with_weakly_dominated_strategies
|
228
|
+
gs.valid_deviation?(@deny, @accept).should_not be_nil
|
229
|
+
end
|
230
|
+
|
231
|
+
it ".valid_deviation? should not be nil if the strategy and the deviation belongs to the same player" do
|
232
|
+
gs = game_with_weakly_dominated_strategies
|
233
|
+
gs.valid_deviation?(@deny, @generic).should == nil
|
234
|
+
end
|
235
|
+
|
236
|
+
# Employer: | Task Specific | Generic
|
237
|
+
# You: -----------------|---------------------|------------------
|
238
|
+
# | |
|
239
|
+
# Deny Training | (-2, -1) | (-2, -1)
|
240
|
+
# ------------------------|---------------------|------------------
|
241
|
+
# Accept Training | (1, 4) | (5, -2)
|
242
|
+
# ------------------------|---------------------|------------------
|
243
|
+
it ".compare_deviating_strategy should return a list of weakly dominated strategies" do
|
244
|
+
# in the above game generic training is weakly dominated
|
245
|
+
gs = game_with_weakly_dominated_strategies
|
246
|
+
gs.compare_deviating_strategy(@deny, @accept).should == :strictly_dominated
|
247
|
+
end
|
248
|
+
|
249
|
+
it ".deviating_strategies should return a list of deviations" do
|
250
|
+
gen_strat_payoff = @gs.strategy_profiles.detect{|x| x[:strategy][:name] == @generic[:name]}
|
251
|
+
task_strat_payoff = @gs.strategy_profiles.detect{|x| x[:strategy][:name] == @task[:name]}
|
252
|
+
@gs.deviating_strategies(gen_strat_payoff).include?(gen_strat_payoff[:strategy]).should == false
|
253
|
+
@gs.deviating_strategies(gen_strat_payoff).include?(task_strat_payoff[:strategy]).should == true
|
254
|
+
end
|
255
|
+
|
256
|
+
it ".player_from_strategy should get the player for a particular strategy" do
|
257
|
+
@gs.player_from_strategy(@accept).should eq @employee
|
258
|
+
end
|
259
|
+
|
260
|
+
it ".best_response should be the action of a player that is best for him given his beliefs about the other's action" do
|
261
|
+
task_strat_payoff = @gs.strategy_profiles.detect{|x| x[:strategy][:name] == @task[:name]}
|
262
|
+
accept_strat_payoff = @gs.strategy_profiles.detect{|x| x[:strategy][:name] == @accept[:name]}
|
263
|
+
@gs.best_response_against(task_strat_payoff)[:strategy][:name].should == accept_strat_payoff[:strategy][:name]
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
it ".player_enum should get a list of player enumerators" do
|
268
|
+
@gs.player_enum.include?(@employer).should == true
|
269
|
+
end
|
270
|
+
|
271
|
+
it ".strategies_by_player should get a list of strategies by player" do
|
272
|
+
@gs.strategies_by_player(@employee).include?(@accept).should == true
|
273
|
+
end
|
274
|
+
|
275
|
+
it ".nash should return a list of strategy profiles that are nash equilibriums" do
|
276
|
+
@gs.nash.include?([@accept[:name],@task[:name]]).should == true
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Player do
|
4
|
+
it "should check for an required keys when initializing" do
|
5
|
+
players=Player.new
|
6
|
+
expect {
|
7
|
+
players << {notname: "test1", description: "test2"}
|
8
|
+
}.to raise_error(RuntimeError,/Name required/)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should allow and insert when name and description are present" do
|
12
|
+
players=Player.new
|
13
|
+
expect {
|
14
|
+
players << {name: "test1", description: "test2"}
|
15
|
+
players << {name: "test3", description: "test4"}
|
16
|
+
}.to_not raise_error
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StrategyProfile do
|
4
|
+
#
|
5
|
+
# Employer: | Task Specific | Generic
|
6
|
+
# You: -----------------|---------------------|------------------
|
7
|
+
# | |
|
8
|
+
# Deny Training | (-2, 0) | (-2, -1)
|
9
|
+
# ------------------------|---------------------|------------------
|
10
|
+
# Accept Training | (1, 4) | (5, -2)
|
11
|
+
# ------------------------|---------------------|------------------
|
12
|
+
before (:each) do
|
13
|
+
@strategies = Strategy.new [{name: "Accept Training"}, {name: "Generic Training"},{name: "Task Specific Training"},{name: "Deny Training"}]
|
14
|
+
@accept = @strategies.find{|x| x[:name] == "Accept Training"}
|
15
|
+
@generic = @strategies.find{|x| x[:name] == "Generic Training"}
|
16
|
+
@task_specific = @strategies.find{|x| x[:name] == "Task Specific Training"}
|
17
|
+
@deny = @strategies.find{|x| x[:name] == "Deny Training"}
|
18
|
+
@employer = Player.new name: "John Smith"
|
19
|
+
@employee = Player.new name: "John Smith"
|
20
|
+
end
|
21
|
+
# hash of stategy, matchup, player, payoff
|
22
|
+
it "should collect matchups" do
|
23
|
+
@strategy_profile = StrategyProfile.new
|
24
|
+
@strategy_profile << {id: 1, strategy: @accept, payoff: -2, player: @employer}
|
25
|
+
@strategy_profile << {id: 1, strategy: @generic, payoff: 5, player: @employee}
|
26
|
+
@strategy_profile.count.should == 2
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Strategy do
|
4
|
+
|
5
|
+
it "should check for an required keys when initializing" do
|
6
|
+
strategies=Strategy.new
|
7
|
+
expect {
|
8
|
+
strategies << {notname: "test1", description: "test2"}
|
9
|
+
}.to raise_error(RuntimeError,/Name required/)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should allow and insert when name and description are present" do
|
13
|
+
strategies=Strategy.new
|
14
|
+
expect {
|
15
|
+
strategies << {name: "test1", description: "test2"}
|
16
|
+
strategies << {name: "test3", description: "test4"}
|
17
|
+
}.to_not raise_error
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#this file is copied to spec/ when you run 'rails generate rspec:install'
|
2
|
+
ENV["RAILS_ENV"] ||= 'test'
|
3
|
+
#require File.expand_path("../../config/environment", __FILE__)
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/minigame/array_of_hashes.rb')
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/minigame/strategy.rb')
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/minigame/strategy_profile.rb')
|
7
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/minigame/player.rb')
|
8
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/minigame/gameable.rb')
|
9
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/minigame/game.rb')
|
10
|
+
require 'rspec/autorun'
|
11
|
+
|
12
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
13
|
+
# in spec/support/ and its subdirectories.
|
14
|
+
#Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
15
|
+
|
16
|
+
Dir["../app/models/*.rb"].each {|f| require_relative f}
|
17
|
+
RSpec.configure do |config|
|
18
|
+
# == Mock Framework
|
19
|
+
#
|
20
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
21
|
+
#
|
22
|
+
# config.mock_with :mocha
|
23
|
+
# config.mock_with :flexmock
|
24
|
+
# config.mock_with :rr
|
25
|
+
config.mock_with :rspec
|
26
|
+
|
27
|
+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
28
|
+
#config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
29
|
+
|
30
|
+
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
31
|
+
# examples within a transaction, remove the following line or assign false
|
32
|
+
# instead of true.
|
33
|
+
# next line doesn't work with database cleaner
|
34
|
+
#config.use_transactional_fixtures = true
|
35
|
+
|
36
|
+
# If true, the base class of anonymous controllers will be inferred
|
37
|
+
# automatically. This will be the default behavior in future versions of
|
38
|
+
# rspec-rails.
|
39
|
+
#config.infer_base_class_for_anonymous_controllers = false
|
40
|
+
#config.include FactoryGirl::Syntax::Methods
|
41
|
+
|
42
|
+
#config.before(:suite) do
|
43
|
+
# DatabaseCleaner.strategy = :transaction
|
44
|
+
# DatabaseCleaner.clean_with(:truncation)
|
45
|
+
#end
|
46
|
+
#config.before(:each) do
|
47
|
+
# DatabaseCleaner.start
|
48
|
+
#end
|
49
|
+
|
50
|
+
#config.after(:each) do
|
51
|
+
# DatabaseCleaner.clean
|
52
|
+
#end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
data/test/test_minigame.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'minigame'
|
2
2
|
require 'minigame/player'
|
3
|
-
require 'debugger'
|
4
3
|
require 'minitest/autorun'
|
5
4
|
require 'minitest/unit'
|
5
|
+
#require 'debugger'
|
6
6
|
|
7
7
|
class MiniGameTest < Minitest::Test
|
8
8
|
class Game
|
@@ -94,8 +94,8 @@ class MiniGameTest < Minitest::Test
|
|
94
94
|
|
95
95
|
def test_weakly_dominated_list
|
96
96
|
game = Game.new
|
97
|
-
game.strategy_profiles =
|
98
|
-
assert_equal [{:name=>"Generic Training"}
|
97
|
+
game.strategy_profiles = new_weak_profiles
|
98
|
+
assert_equal [{:name=>"Generic Training"}], game.weakly_dominated_list
|
99
99
|
end
|
100
100
|
|
101
101
|
def test_nash
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minigame
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- W Watson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,15 +52,30 @@ dependencies:
|
|
52
52
|
- - ! '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description: A game theory library
|
56
70
|
email:
|
57
|
-
-
|
71
|
+
- me@wwatson.me
|
58
72
|
executables: []
|
59
73
|
extensions: []
|
60
74
|
extra_rdoc_files: []
|
61
75
|
files:
|
62
76
|
- .gitignore
|
63
77
|
- .rvmrc
|
78
|
+
- .travis.yml
|
64
79
|
- Gemfile
|
65
80
|
- Gemfile.lock
|
66
81
|
- LICENSE.txt
|
@@ -70,14 +85,20 @@ files:
|
|
70
85
|
- lib/minigame/array_of_hashes.rb
|
71
86
|
- lib/minigame/game.rb
|
72
87
|
- lib/minigame/gameable.rb
|
73
|
-
- lib/minigame/payoff.rb
|
74
88
|
- lib/minigame/player.rb
|
75
89
|
- lib/minigame/strategy.rb
|
76
90
|
- lib/minigame/strategy_profile.rb
|
77
91
|
- lib/minigame/version.rb
|
78
92
|
- minigame.gemspec
|
93
|
+
- spec/models/array_of_hashes_spec.rb
|
94
|
+
- spec/models/gameable_spec.rb
|
95
|
+
- spec/models/games_spec.rb
|
96
|
+
- spec/models/player_spec.rb
|
97
|
+
- spec/models/strategy_profile_spec.rb
|
98
|
+
- spec/models/strategy_spec.rb
|
99
|
+
- spec/spec_helper.rb
|
79
100
|
- test/test_minigame.rb
|
80
|
-
homepage:
|
101
|
+
homepage: https://github.com/wavell/minigame
|
81
102
|
licenses:
|
82
103
|
- MIT
|
83
104
|
metadata: {}
|
@@ -102,4 +123,11 @@ signing_key:
|
|
102
123
|
specification_version: 4
|
103
124
|
summary: A minimalistic game theory library that computes nash equilibrium
|
104
125
|
test_files:
|
126
|
+
- spec/models/array_of_hashes_spec.rb
|
127
|
+
- spec/models/gameable_spec.rb
|
128
|
+
- spec/models/games_spec.rb
|
129
|
+
- spec/models/player_spec.rb
|
130
|
+
- spec/models/strategy_profile_spec.rb
|
131
|
+
- spec/models/strategy_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
105
133
|
- test/test_minigame.rb
|
data/lib/minigame/payoff.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'data_mapper'
|
2
|
-
#class Payoff < ActiveRecord::Base
|
3
|
-
# belongs_to :player
|
4
|
-
# belongs_to :matchup
|
5
|
-
# attr_accessible :payoff
|
6
|
-
#end
|
7
|
-
class Payoff
|
8
|
-
include DataMapper::Resource
|
9
|
-
belongs_to :player
|
10
|
-
belongs_to :matchup
|
11
|
-
#attr_accessible :payoff
|
12
|
-
end
|