minigame 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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YWY0MTkxZDc2ZTg1NWExMTgwODMyMDAwNjEwNmQ3YzZiODJmYTM1Zg==
5
+ data.tar.gz: !binary |-
6
+ NGYzYTBiYTQ0ODFkNDg5OWVlYWRiZDJmYTJjNDIyNzZlY2VmNzk3Yw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ OTM4MDgzZGY3MWNlNWUwOWY0ZGE0ZmM2ZDg4NWQ0NmQxNzU0YTc2Y2JiNWQy
10
+ MWMzMTA3MmY5MDk1Mjk0ZjVjMGNkODNlYTU0OGU2ZjJlMGYzYTdjYTBmOWE3
11
+ NTA2OWMzMDhhNGU2OGFmYjFhNzA5ZGJiNjVjZGMyODg2MThiYTg=
12
+ data.tar.gz: !binary |-
13
+ YjhlN2JiMzQzMjRmZDBmOGFjODQyMjA5Nzk5ZjRkZDAwZGYyN2Y4NGM3YjQ4
14
+ ZjZhM2NjMWI4YmNlNWQwZjc3OTVkMDk3ZDQwODhiNGJjNmVhMzc4YWUxNjI1
15
+ ZWJhZjk5YTNmMzUyMWMyMzUwYWNkY2ZiYTNjMzgzNzk1Y2RjNzA=
data/.gitignore ADDED
@@ -0,0 +1,99 @@
1
+ #----------------------------------------------------------------------------
2
+ # Ignore these files when commiting to a git repository.
3
+ #
4
+ # See http://help.github.com/ignore-files/ for more about ignoring files.
5
+ #
6
+ # The original version of this file is found here:
7
+ # https://github.com/RailsApps/rails3-application-templates/raw/master/files/gitignore.txt
8
+ #
9
+ # Corrections? Improvements? Create a GitHub issue:
10
+ # http://github.com/RailsApps/rails3-application-templates/issues
11
+ #----------------------------------------------------------------------------
12
+
13
+ # bundler state
14
+ /.bundle
15
+ /vendor/bundle/
16
+
17
+ # minimal Rails specific artifacts
18
+ db/*.sqlite3
19
+ /log/*.log
20
+ /tmp
21
+
22
+ # various artifacts
23
+ **.war
24
+ *.rbc
25
+ *.sassc
26
+ .rspec
27
+ .redcar/
28
+ .sass-cache
29
+ /config/config.yml
30
+ /config/database.yml
31
+ /coverage.data
32
+ /coverage/
33
+ /db/*.javadb/
34
+ /db/*.sqlite3-journal
35
+ /doc/api/
36
+ /doc/app/
37
+ /doc/features.html
38
+ /doc/specs.html
39
+ /public/cache
40
+ /public/stylesheets/compiled
41
+ /public/system
42
+ /spec/tmp/*
43
+ /cache
44
+ /capybara*
45
+ /capybara-*.html
46
+ /gems
47
+ /rerun.txt
48
+ /spec/requests
49
+ /spec/routing
50
+ /spec/views
51
+ /specifications
52
+ # If you find yourself ignoring temporary files generated by your text editor
53
+ # or operating system, you probably want to add a global ignore instead:
54
+ # git config --global core.excludesfile ~/.gitignore_global
55
+ #
56
+ # Here are some files you may want to ignore globally:
57
+
58
+ # scm revert files
59
+ **.orig
60
+
61
+ # Mac finder artifacts
62
+ .DS_Store
63
+ *~
64
+
65
+ # Netbeans project directory
66
+ /nbproject/
67
+
68
+ # RubyMine project files
69
+ .idea
70
+
71
+ # Textmate project files
72
+ /*.tmproj
73
+
74
+ # vim artifacts
75
+ **.swp
76
+ **.swo
77
+
78
+ # Windows artifacts
79
+ Thumbs.db
80
+
81
+ # ctags artifact
82
+ /tags
83
+
84
+ *.rbc
85
+ *.sassc
86
+ .sass-cache
87
+ capybara-*.html
88
+ .rspec
89
+ /.bundle
90
+ /vendor/bundle
91
+ /log/*
92
+ /tmp/*
93
+ /db/*.sqlite3
94
+ /public/system/*
95
+ /coverage/
96
+ /spec/tmp/*
97
+ **.orig
98
+ rerun.txt
99
+ pickle-email-*.html
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3-p327@minitest-dm --create
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in minigame.gemspec
4
+ gemspec
5
+ gem 'debugger'
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ minigame (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ columnize (0.3.6)
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)
16
+ minitest (5.0.6)
17
+ rake (10.1.0)
18
+
19
+ PLATFORMS
20
+ ruby
21
+
22
+ DEPENDENCIES
23
+ bundler (~> 1.3)
24
+ debugger
25
+ minigame!
26
+ minitest
27
+ rake
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Wavell Watson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # MiniGame
2
+ ## Simplistic Game Theory Library in Ruby
3
+
4
+ Usage:
5
+
6
+ To create new players:
7
+ ```
8
+ employer = Player.new name: "IBM"
9
+ employee = Player.new name: "John Smith
10
+ ```
11
+
12
+ To create a new list of strategies for all players in a game:
13
+ ```
14
+ strategies = Strategy.new [{name: "Accept Training"},
15
+ {name: "Generic Training"},
16
+ {name: "Task Specific Training"},
17
+ {name: "Deny Training"}]
18
+ ```
19
+
20
+ To create new StrategyProfiles:
21
+ ```
22
+
23
+ # Regular training game -- generic strictly dominated
24
+ #
25
+ # Employer: | Task Specific | Generic
26
+ # Employee: ---------------|---------------------|------------------
27
+ # | |
28
+ # Deny Training | (-2, 0) | (-2, -1)
29
+ # ------------------------|---------------------|------------------
30
+ # Accept Training | (1, 4) | (5, -2)
31
+ # ------------------------|---------------------|------------------
32
+ strategy_profile = StrategyProfile.new
33
+ strategy_profile << {id: 1, strategy: generic, payoff: -2, player: employer}
34
+ strategy_profile << {id: 1, strategy: accept, payoff: 5, player: employee}
35
+
36
+ strategy_profile << {id: 2, strategy: generic, payoff: -1, player: employer}
37
+ strategy_profile << {id: 2, strategy: deny, payoff: -2, player: employee}
38
+
39
+ strategy_profile << {id: 3, strategy: task, payoff: 0, player: employer}
40
+ strategy_profile << {id: 3, strategy: deny, payoff: -2, player: employee}
41
+
42
+ strategy_profile << {id: 4, strategy: task, payoff: 4, player: employer}
43
+ strategy_profile << {id: 4, strategy: accept, payoff: 1, player: employee}
44
+ ```
45
+
46
+ To create a new game:
47
+ ```
48
+ gs = Game.new
49
+ gs.strategy_profiles = strategy_profile
50
+ ```
51
+
52
+ To get the list of strictly dominated strategies:
53
+ ```
54
+ gs.strictly_dominated_list
55
+
56
+ [{:name=>"Generic Training"}, {:name=>"Deny Training"}]
57
+
58
+ ```
59
+
60
+ To get the list a weakly dominated strategies:
61
+ ```
62
+ # training game -- generic weakly dominated (not realistic)
63
+ #
64
+ # Employer: | Task Specific | Generic
65
+ # Employer: ----------------|---------------------|------------------
66
+ # | |
67
+ # Deny Training | (-2, -1) | (-2, -1)
68
+ # ------------------------|---------------------|------------------
69
+ # Accept Training | (1, 4) | (5, -2)
70
+ # ------------------------|---------------------|------------------
71
+ gs.weakly_dominated_list
72
+
73
+ [{:name=>"Generic Training"}, {:name=>"Deny Training"}]
74
+
75
+ ```
76
+
77
+ To get the list strategy profiles that are nash equilibria:
78
+ ```
79
+ gs.nash
80
+
81
+ [["Accept Training", "Task Specific Training"]]
82
+
83
+ ```
84
+
85
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
@@ -0,0 +1,68 @@
1
+ module ArrayOfHashes
2
+ include Enumerable
3
+
4
+ def initialize(*array_of_hashes)
5
+ array_of_hashes = [array_of_hashes].flatten
6
+ @keys ||=[]
7
+ # checks to see if required_keys was called
8
+ # as a singleton method on the included class
9
+ @keys = self.class.keys if self.class.keys
10
+ check_array(array_of_hashes)
11
+ @array_of_hashes=array_of_hashes
12
+ end
13
+
14
+ def each(&block)
15
+ @array_of_hashes.each do |matchup|
16
+ block.call(matchup)
17
+ end
18
+ end
19
+
20
+ # creates a singleton method on the included class
21
+ # which avoids having to put 'required_keys' into the
22
+ # included classes initialize method
23
+ def self.included(klass)
24
+ class << klass
25
+ attr_accessor :keys
26
+ def required_keys(*keys)
27
+ # force single keys to be an array
28
+ keys = [keys].flatten
29
+ keys.each do |x|
30
+ raise 'Not a symbol' if !keys[0].is_a? Symbol
31
+ end
32
+ @keys=keys
33
+ end
34
+ end
35
+ end
36
+
37
+ # required keys available on the instance
38
+ def required_keys(*keys)
39
+ # force single keys to be an array
40
+ keys = [keys].flatten
41
+ keys.each do |x|
42
+ raise 'Not a symbol' if !keys[0].is_a? Symbol
43
+ end
44
+ @keys = keys
45
+ end
46
+
47
+ def required_keys=(keys)
48
+ required_keys(keys)
49
+ end
50
+
51
+ def check_array(array_of_hashes)
52
+ raise 'Not an array' if !array_of_hashes.is_a? Array
53
+ array_of_hashes.each{|x|check_keys(x)}
54
+ end
55
+
56
+ def check_keys(val)
57
+ raise "Not a Hash" if val.class != Hash
58
+ @keys.each do |x|
59
+ raise "#{x.to_s.capitalize} required" if !val.keys.include?(x)
60
+ end
61
+ end
62
+
63
+ def <<(val)
64
+ check_keys(val)
65
+ @array_of_hashes << val
66
+ end
67
+
68
+ end
@@ -0,0 +1,3 @@
1
+ class Game
2
+ include Gameable
3
+ end
@@ -0,0 +1,273 @@
1
+ module Gameable
2
+ attr_accessor :strategy_profiles
3
+
4
+ # any set of strategies that are best responses for each other are nash equilibriums
5
+ def nash
6
+ # assume the same number of strategies for each side for now
7
+ # get one player
8
+ firstp = player_enum.first
9
+ # get opposing player
10
+ opp_p = player_enum[1]
11
+ # get strategies for the player
12
+ pstrats = player_strategy_profiles(firstp)
13
+ oppstrats = player_strategy_profiles(opp_p)
14
+ # loop through first strategies and get best responses
15
+ nash ||=[]
16
+ nash = pstrats.inject([]) do |acc, strat|
17
+ #loop through opposing strategies
18
+ oppstrats.map do |opp_strat|
19
+ br = best_response_against(strat)[:strategy][:name]
20
+ br_opposing = best_response_against(opp_strat)[:strategy][:name]
21
+ # best response for both sides that match each other are nash equilibriums
22
+ if br == opp_strat[:strategy][:name] && br_opposing == strat[:strategy][:name]
23
+ acc << [br,br_opposing]
24
+ end
25
+ end
26
+ acc
27
+ end
28
+ nash.uniq
29
+ end
30
+
31
+ def best_response_against(opponent_strategy)
32
+ best_resp=[]
33
+ # get opponent player from strategy
34
+ opp_player = player_from_strategy(opponent_strategy[:strategy]).first
35
+ # get player from strategy
36
+ player = players.detect{|x| x[:name] != opp_player[:name]}
37
+ strat_payoff_list ||=[]
38
+ # only collect possible payoffs that are opposed to the passed in opponent's strategy
39
+ strat_payoff_list = player_strategy_profiles(player).inject([]) do |acc, str|
40
+ if opposing_player_strategy_profile(str)[:strategy][:name]==opponent_strategy[:strategy][:name]
41
+ acc << str
42
+ end
43
+ acc
44
+ end
45
+ best_resp = strat_payoff_list.sort{|x| x[:payoff]}.reverse
46
+ # return top performer, later need to return all strategies that are equal in payoff
47
+ best_resp.first
48
+ end
49
+
50
+ def strictly_dominated_list
51
+ dominated_list ||=[]
52
+ players.each do |player|
53
+ dominated_list << player_strategy_profiles(player).inject([]) do |acc, strat|
54
+ # if count of total strategies for opposing player = count of better payoffs, then current strategy is strictly dominated
55
+ dev = deviating_strategies(strat)
56
+ new_strat_list =[]
57
+ dev.each do |dev_strat|
58
+ if compare_deviating_strategy(strat[:strategy], dev_strat) == :strictly_dominated
59
+ new_strat_list << strat[:strategy]
60
+ end
61
+ end
62
+ acc << new_strat_list
63
+ end
64
+ end
65
+ dominated_list.flatten.uniq
66
+ end
67
+
68
+ def weakly_dominated_list
69
+ dominated_list ||=[]
70
+ players.each do |player|
71
+ dominated_list << player_strategy_profiles(player).inject([]) do |acc, strat|
72
+ dev = deviating_strategies(strat)
73
+ dev.each do |dev_strat|
74
+ if compare_deviating_strategy(strat[:strategy], dev_strat) == :weakly_dominated
75
+ acc << strat[:strategy]
76
+ else
77
+ acc
78
+ end
79
+ end
80
+ end
81
+ end
82
+ dominated_list.flatten.uniq
83
+ end
84
+
85
+ def strategies_by_player(player)
86
+ strats = strategy_profiles.inject([]) do |acc, strat|
87
+ if strat[:player] == player
88
+ acc << strat[:strategy]
89
+ else
90
+ acc
91
+ end
92
+ end
93
+ strats.uniq
94
+ end
95
+
96
+ # retrieve a player assigned to the strategy
97
+ def player_from_strategy(strategy)
98
+ stratp = strategy_profiles.detect{|x| x[:strategy][:name] == strategy[:name]}
99
+ stratp[:player]
100
+ end
101
+
102
+ # valid deviations must belong to the same player
103
+ def deviating_strategies(strategy_payoff)
104
+ strategies(strategy_payoff[:player].first).select{|x| x[:name] != strategy_payoff[:strategy][:name]}
105
+ end
106
+
107
+ # valid deviations must belong to the same player
108
+ def valid_deviation?(strategy, deviation)
109
+ selected_profile_list = self.strategy_profiles.detect{|x| x[:strategy][:name] == strategy[:name]}
110
+ matching_strategy_deviation = self.strategy_profiles.detect{|x| x[:strategy][:name] == deviation[:name] && x[:player] == selected_profile_list[:player] }
111
+ end
112
+
113
+ # for a strategy, take each strategy profile and match it against
114
+ # it's corresonding but deviating strategy profile
115
+ def compare_deviating_strategy(strategy, deviation)
116
+ raise "invalid deviation for submitted strategy" if !valid_deviation?(strategy, deviation)
117
+
118
+ one_equal = false
119
+ one_better = false
120
+ selected_profile_list = self.strategy_profiles.select{|x| x[:strategy][:name] == strategy[:name]}
121
+ # loop through strategy profiles from the strategy
122
+ selected_profile_list.each do |strat|
123
+ # store strat payoff e.g. -2
124
+ strat_payoff = strat[:payoff]
125
+ opposing_strategy = opposing_player_strategy_profile(strat)
126
+ # get deviating profile's payoff that corresponds to opposing strategy
127
+ deviating_profile = self.strategy_profiles.select do |x|
128
+ x[:strategy][:name] == deviation[:name] && opposing_player_strategy_profile(x)[:strategy][:name] == opposing_strategy[:strategy][:name]
129
+ end
130
+ # compare payoffs
131
+ # if strategy payoff is better than even just one deviating
132
+ # payoff, the strategy is not dominated
133
+ if strat_payoff > deviating_profile[0][:payoff]
134
+ return :not_dominated
135
+ # if payoff for deviating strategy is better, remember that fact
136
+ elsif strat_payoff < deviating_profile[0][:payoff]
137
+ one_better = true
138
+ # if payoff for deviating strategy is equal, remember that fact
139
+ elsif strat_payoff == deviating_profile[0][:payoff]
140
+ one_equal = true
141
+ end
142
+ end
143
+ # if made it here, the deviating profile has either all better, or some better and some equal payoffs
144
+ # if all deviation profiles are equal or better, strategy is weakly dominated by the deviation
145
+ return :weakly_dominated if one_equal
146
+ # if all deviating profiles are better, strategy is dominated by the deviation
147
+ return :strictly_dominated if one_better
148
+
149
+ raise "nothing better, equal, or worse"
150
+ end
151
+
152
+
153
+ def equal_moves(strat)
154
+ complementary_moves(strat).select{|x|x["payoff"]==strat["payoff"]}
155
+ end
156
+
157
+ def payoff(strategy, player)
158
+ strategy_profile = [@strategy_profiles.select{|x| x[:strategy]==strategy && x[:player]==player}].flatten.first
159
+ strategy_profile[:payoff] if strategy_profile
160
+ end
161
+
162
+ def player_names
163
+ players.inject([]) do |acc, x|
164
+ acc << x[:name]
165
+ end
166
+ end
167
+
168
+ def players
169
+ # need inject to remove duplicates
170
+ @strategy_profiles.inject([]) do |acc, x|
171
+ if acc.nil? || acc.empty?
172
+ # should be only one player per strategy_profile
173
+ acc << x[:player].first
174
+ elsif !acc.include?(x[:player].first)
175
+ acc << x[:player].first
176
+ else
177
+ acc
178
+ end
179
+ end
180
+ end
181
+
182
+ def player_enum
183
+ # need inject to remove duplicates
184
+ player_enums = @strategy_profiles.inject([]) do |acc, x|
185
+ acc << x[:player]
186
+ end
187
+ player_enums.uniq
188
+ end
189
+
190
+ def opposing_players(strategy_profile)
191
+ players - [strategy_profile[:player].first]
192
+ end
193
+
194
+ def player_strategy_profiles(player)
195
+ # find some way to standardize what a player is ...
196
+ # possibly plural is enumerable, singular is a hash
197
+ # same problem with other classes
198
+ if player.class == Player
199
+ @strategy_profiles.select{|x| x[:player] == player}
200
+ else
201
+ @strategy_profiles.select{|x| x[:player].first == player}
202
+ end
203
+ end
204
+
205
+ def strategies(player)
206
+ player_strategy_profiles(player).reduce([]){|acc,strat| acc << strat[:strategy]}.uniq
207
+ end
208
+
209
+ # returns the strategy associated with the passed in profile
210
+ # belonging to the opposing player
211
+ def opposing_player_strategy_profile(strategy_profile)
212
+ # list of players
213
+ # get all strategies profiles for the first player that does not equal
214
+ # the passed in player
215
+ opposing_player = opposing_players(strategy_profile).first
216
+ # return the first strategy with the strategy profile id equal
217
+ # to the passed in strategy profile id
218
+ opposing_strategies = player_strategy_profiles(opposing_player)
219
+ opposing_strategies.select{|x| x[:id]==strategy_profile[:id]}.first
220
+ end
221
+
222
+ # all other moves other than the passed in strategy (for the player)
223
+ def complementary_moves(strategy_profile)
224
+ psp = player_strategy_profiles(strategy_profile[:player])
225
+ psp.select do |x|
226
+ # not the current strategy
227
+ if x[:strategy] != strategy_profile[:strategy] &&
228
+ # only return profiles matched vs original opposing strategy
229
+ opposing_player_strategy_profile(strategy_profile)[:strategy] ==
230
+ opposing_player_strategy_profile(x)[:strategy]
231
+ true
232
+ else
233
+ false
234
+ end
235
+ end
236
+ end
237
+
238
+ # all other payoffs other than the passed in strategy (for the player)
239
+ def deviating_payoffs(strategy_profile)
240
+ psp = player_strategy_profiles(strategy_profile[:player])
241
+ dev_pay = psp.select do |x|
242
+ # not the current strategy
243
+ if x[:strategy] != strategy_profile[:strategy] &&
244
+ # only payoffs matched with original opposing strategy are
245
+ # considered deviating payoffs
246
+ opposing_player_strategy_profile(strategy_profile)[:strategy] ==
247
+ opposing_player_strategy_profile(x)[:strategy]
248
+ true
249
+ else
250
+ false
251
+ end
252
+ end
253
+ dev_pay.reduce([]){|acc,p| acc << p}
254
+ end
255
+
256
+ alias_method :other_moves, :complementary_moves
257
+
258
+ # this should list all of the deviating payoffs for the opponent's strategy
259
+ # within the passed in strategy profile. Only the payoffs corresponding
260
+ # to the opponent's strategy are considered deviating payoffs
261
+ def better_payoffs?(strategy_profile)
262
+ deviating_payoffs(strategy_profile).select{|x| x[:payoff] > strategy_profile[:payoff]}
263
+ end
264
+
265
+ def better_or_equal_payoffs?(strategy_profile)
266
+ deviating_payoffs(strategy_profile).select{|x| x[:payoff] >= strategy_profile[:payoff]}
267
+ end
268
+
269
+ def worse_payoffs?(strategy_profile)
270
+ deviating_payoffs(strategy_profile).select{|x| x[:payoff] < strategy_profile[:payoff]}
271
+ end
272
+
273
+ end
@@ -0,0 +1,12 @@
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
@@ -0,0 +1,5 @@
1
+ require 'minigame/array_of_hashes'
2
+ class Player
3
+ include ArrayOfHashes
4
+ required_keys :name
5
+ end
@@ -0,0 +1,4 @@
1
+ class Strategy
2
+ include ArrayOfHashes
3
+ required_keys :name
4
+ end
@@ -0,0 +1,4 @@
1
+ class StrategyProfile
2
+ include ArrayOfHashes
3
+ required_keys :id, :strategy, :payoff, :player
4
+ end
@@ -0,0 +1,3 @@
1
+ module Minigame
2
+ VERSION = "0.0.1"
3
+ end
data/lib/minigame.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "minigame/version"
2
+ require 'minigame/array_of_hashes'
3
+ require 'minigame/gameable'
4
+ require 'minigame/strategy'
5
+ require 'minigame/strategy_profile'
6
+
7
+ module MiniGame
8
+ include Gameable
9
+ end
data/minigame.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'minigame/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "minigame"
8
+ spec.version = Minigame::VERSION
9
+ spec.authors = ["Wavell Watson"]
10
+ spec.email = ["wavell.watson@gmail.com"]
11
+ spec.description = %q{A game theory library}
12
+ spec.summary = %q{A minimalistic game theory library that computes nash equilibrium}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest"
24
+
25
+ end
@@ -0,0 +1,107 @@
1
+ require 'minigame'
2
+ require 'minigame/player'
3
+ require 'debugger'
4
+ require 'minitest/autorun'
5
+ require 'minitest/unit'
6
+
7
+ class MiniGameTest < Minitest::Test
8
+ class Game
9
+ include MiniGame
10
+ end
11
+
12
+ def new_players
13
+ employer = Player.new name: "IBM"
14
+ employee = Player.new name: "John Smith"
15
+ [employer, employee]
16
+ end
17
+ def new_strategies
18
+ strategies = Strategy.new [{name: "Accept Training"},
19
+ {name: "Generic Training"},
20
+ {name: "Task Specific Training"},
21
+ {name: "Deny Training"}]
22
+ # send back an array of strategies
23
+ strategies.inject([]){|x,y| x << y}
24
+ end
25
+
26
+ def new_profiles
27
+ employer, employee = new_players
28
+ accept, generic, task, deny = new_strategies
29
+ strategy_profile = StrategyProfile.new
30
+ strategy_profile << {id: 1, strategy: generic, payoff: -2, player: employer}
31
+ strategy_profile << {id: 1, strategy: accept, payoff: 5, player: employee}
32
+
33
+ strategy_profile << {id: 2, strategy: generic, payoff: -1, player: employer}
34
+ strategy_profile << {id: 2, strategy: deny, payoff: -2, player: employee}
35
+
36
+ strategy_profile << {id: 3, strategy: task, payoff: 0, player: employer}
37
+ strategy_profile << {id: 3, strategy: deny, payoff: -2, player: employee}
38
+
39
+ strategy_profile << {id: 4, strategy: task, payoff: 4, player: employer}
40
+ strategy_profile << {id: 4, strategy: accept, payoff: 1, player: employee}
41
+ strategy_profile
42
+ end
43
+
44
+ def new_weak_profiles
45
+ employer, employee = new_players
46
+ accept, generic, task, deny = new_strategies
47
+ strategy_profile = StrategyProfile.new
48
+ strategy_profile << {id: 1, strategy: generic, payoff: -2, player: employer}
49
+ strategy_profile << {id: 1, strategy: accept, payoff: 5, player: employee}
50
+
51
+ strategy_profile << {id: 2, strategy: generic, payoff: -1, player: employer}
52
+ strategy_profile << {id: 2, strategy: deny, payoff: -2, player: employee}
53
+
54
+ # not realistic
55
+ strategy_profile << {id: 3, strategy: task, payoff: -1, player: employer}
56
+ strategy_profile << {id: 3, strategy: deny, payoff: -2, player: employee}
57
+
58
+ strategy_profile << {id: 4, strategy: task, payoff: 4, player: employer}
59
+ strategy_profile << {id: 4, strategy: accept, payoff: 1, player: employee}
60
+ end
61
+
62
+ def test_mixin_game
63
+ game = Game.new
64
+ assert_equal Game, game.class
65
+ end
66
+
67
+ def test_new_player
68
+ employer, employee = new_players
69
+ assert_equal "IBM", employer.first[:name]
70
+ end
71
+
72
+ def test_new_strategy
73
+ strategies = new_strategies
74
+ assert_equal 4, strategies.count
75
+
76
+ end
77
+
78
+ def test_new_profiles
79
+ profiles = new_profiles
80
+ assert_equal 8, profiles.count
81
+ end
82
+
83
+ def test_game_w_profile
84
+ game = Game.new
85
+ game.strategy_profiles = new_profiles
86
+ assert_equal 8, game.strategy_profiles.count
87
+ end
88
+
89
+ def test_strictly_dominated_list
90
+ game = Game.new
91
+ game.strategy_profiles = new_profiles
92
+ assert_equal [{:name=>"Generic Training"}, {:name=>"Deny Training"}], game.strictly_dominated_list
93
+ end
94
+
95
+ def test_weakly_dominated_list
96
+ game = Game.new
97
+ game.strategy_profiles = new_profiles
98
+ assert_equal [{:name=>"Generic Training"}, {:name=>"Deny Training"}], game.weakly_dominated_list
99
+ end
100
+
101
+ def test_nash
102
+ game = Game.new
103
+ game.strategy_profiles = new_profiles
104
+ assert_equal [["Accept Training", "Task Specific Training"]], game.nash
105
+ end
106
+
107
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: minigame
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Wavell Watson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A game theory library
56
+ email:
57
+ - wavell.watson@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .rvmrc
64
+ - Gemfile
65
+ - Gemfile.lock
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - lib/minigame.rb
70
+ - lib/minigame/array_of_hashes.rb
71
+ - lib/minigame/game.rb
72
+ - lib/minigame/gameable.rb
73
+ - lib/minigame/payoff.rb
74
+ - lib/minigame/player.rb
75
+ - lib/minigame/strategy.rb
76
+ - lib/minigame/strategy_profile.rb
77
+ - lib/minigame/version.rb
78
+ - minigame.gemspec
79
+ - test/test_minigame.rb
80
+ homepage: ''
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.0.3
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: A minimalistic game theory library that computes nash equilibrium
104
+ test_files:
105
+ - test/test_minigame.rb