bitpoker 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ module BitPoker
2
+
3
+ # Module with default rules of the game
4
+ #
5
+ # @author Mckomo
6
+ module Rules
7
+
8
+ ROUNDS = 1000 # Number of rounds to be played in a duel
9
+ MIN_STAKE = 10
10
+ MAX_STAKE = 200
11
+ TIMEOUT = 1 # Timeout of bot response
12
+ CARD_RANGE = 0..4
13
+
14
+ end
15
+
16
+ end
data/test.rb ADDED
File without changes
@@ -0,0 +1,46 @@
1
+ # GlassBot makes all properties visible
2
+ class GlassBot < BitPoker::BotInterface
3
+
4
+ attr_reader :card, :min_card, :max_card, :max_stake, :timeout
5
+
6
+ def introduce( rules )
7
+ @min_card = rules["min_card"]
8
+ @max_card = rules["max_card"]
9
+ @max_stake = rules["max_stake"]
10
+ @timeout = rules["timeout"]
11
+ end
12
+
13
+ def get_card( card )
14
+ @card = card
15
+ end
16
+
17
+ def bet_one( min_stake )
18
+ ( min_stake + @max_stake ) / 2
19
+ end
20
+
21
+ def agree_one( opponent_stake )
22
+ true
23
+ end
24
+
25
+ def bet_two( min_stake, max_stake )
26
+ ( min_stake + @max_stake ) / 2
27
+ end
28
+
29
+ def agree_two( opponent_stake )
30
+ true
31
+ end
32
+
33
+ def showdown( opponent_card )
34
+ @opponent_card = opponent_card
35
+ end
36
+
37
+ def end_of_round( score )
38
+ @score = score
39
+ end
40
+
41
+ def end_of_duel( total_score, opponent_score )
42
+ end
43
+
44
+ end
45
+
46
+
@@ -0,0 +1,14 @@
1
+ class SleepyBot < BitPoker::BotInterface
2
+
3
+ def return_after_sleep( value, timeout )
4
+ sleep( timeout )
5
+ value
6
+ end
7
+
8
+ def raise_exception
9
+ 2 / 0
10
+ end
11
+
12
+ end
13
+
14
+
@@ -0,0 +1,21 @@
1
+ require "test/unit"
2
+ require 'shoulda'
3
+
4
+ require "./lib/bitpoker"
5
+
6
+ class TestBitPoker < Test::Unit::TestCase
7
+
8
+ context "a BitPoker module" do
9
+
10
+ should "be able to load a bot from file" do
11
+
12
+ bot = BitPoker.load_bot( "sleepy_bot", "#{Dir.pwd}/test/support", "" )
13
+
14
+ assert_kind_of BitPoker::BotInterface, bot
15
+ assert_equal "SleepyBot", bot.name
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,59 @@
1
+ require "test/unit"
2
+ require 'shoulda'
3
+ require 'mocha/setup'
4
+
5
+ require "./lib/bitpoker"
6
+
7
+ class TestBot < Test::Unit::TestCase
8
+
9
+ include BitPoker
10
+
11
+ context "a bot interface" do
12
+
13
+ setup do
14
+ @interface = BotInterface.new
15
+ end
16
+
17
+ should "force to implement all required methods" do
18
+
19
+ assert_raise ArgumentError do
20
+ @interface.introduce
21
+ end
22
+
23
+ assert_raise ArgumentError do
24
+ @interface.get_card
25
+ end
26
+
27
+ assert_raise ArgumentError do
28
+ @interface.bet_one
29
+ end
30
+
31
+ assert_raise ArgumentError do
32
+ @interface.agree_one
33
+ end
34
+
35
+ assert_raise ArgumentError do
36
+ @interface.bet_two
37
+ end
38
+
39
+ assert_raise ArgumentError do
40
+ @interface.agree_two
41
+ end
42
+
43
+ assert_raise ArgumentError do
44
+ @interface.showdown
45
+ end
46
+
47
+ assert_raise ArgumentError do
48
+ @interface.end_of_round
49
+ end
50
+
51
+ assert_raise ArgumentError do
52
+ @interface.end_of_duel
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,40 @@
1
+ require "test/unit"
2
+ require 'shoulda'
3
+ require 'mocha/setup'
4
+
5
+ require "./lib/bitpoker"
6
+
7
+ class TestBotProxy < Test::Unit::TestCase
8
+
9
+ include BitPoker
10
+
11
+ context "a BotProxy" do
12
+
13
+ should "load only a bot that implements BotInterface" do
14
+
15
+ bot = BotInterface.new
16
+
17
+ assert_nothing_raised do
18
+ BotProxy.new( bot )
19
+ end
20
+
21
+ assert_raise ArgumentError do
22
+ BotProxy.new( "not_a_bot" )
23
+ end
24
+
25
+ end
26
+
27
+ should "trigger bot action and get it's response" do
28
+
29
+ bot = BotInterface.new
30
+ bot.stubs( :bet_one ).returns( 10 )
31
+
32
+ proxy = BotProxy.new( bot )
33
+
34
+ assert_equal 10, proxy.trigger( :bet_one, 5 )
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,147 @@
1
+ require "test/unit"
2
+ require 'shoulda'
3
+ require "mocha/setup"
4
+
5
+ require './lib/bitpoker'
6
+ require_relative 'support/sleepy_bot'
7
+
8
+
9
+ class TestCroupier < Test::Unit::TestCase
10
+
11
+ include BitPoker
12
+
13
+ context "a croupier" do
14
+
15
+ setup do
16
+ @croupier = Croupier.new
17
+ @bot = BitPoker::BotProxy.new( SleepyBot.new )
18
+ end
19
+
20
+ should "know rules" do
21
+ assert_not_nil @croupier.rules
22
+ end
23
+
24
+ should "be able to use custom rules" do
25
+
26
+ custom_rules = {
27
+ rounds: 100,
28
+ min_stake: 100,
29
+ max_stake: 1000,
30
+ timeout: 100,
31
+ card_range: 5..10
32
+ }
33
+ croupier = Croupier.new( custom_rules )
34
+
35
+ assert_equal custom_rules, croupier.rules
36
+
37
+ end
38
+
39
+ should "be able to share round rules" do
40
+
41
+ custom_rules = {
42
+ rounds: 100,
43
+ min_stake: 100,
44
+ max_stake: 1000,
45
+ timeout: 100,
46
+ card_range: 5..10
47
+ }
48
+
49
+ croupier = Croupier.new( custom_rules )
50
+
51
+ round_rules = {
52
+ "min_card" => 5,
53
+ "max_card" => 10,
54
+ "max_stake" => 1000,
55
+ "timeout" => 100
56
+ }
57
+
58
+ assert_equal round_rules, croupier.round_rules
59
+
60
+ end
61
+
62
+ should "follow game logic" do
63
+ @croupier.kind_of?( GameLogic )
64
+ end
65
+
66
+ should "be able to call bot and get it's response" do
67
+
68
+ bot = BotProxy.new( BotInterface.new )
69
+
70
+ card = 4
71
+ min_stake = 10
72
+ max_stake = 200
73
+ call_stake = 100
74
+
75
+ bot.stubs( :trigger ).with( :get_card, card ).returns( card )
76
+ bot.stubs( :trigger ).with( :bet_one, [min_stake, max_stake] ).returns( min_stake )
77
+ bot.stubs( :trigger ).with( :agree_one, call_stake ).returns( true )
78
+
79
+ assert_equal card, @croupier.call( bot, :get_card, card )
80
+ assert_equal min_stake, @croupier.call( bot, :bet_one, [min_stake, max_stake] )
81
+ assert @croupier.call( bot, :agree_one, call_stake )
82
+
83
+ end
84
+
85
+ should "disqualify bot when exceeded timeout" do
86
+
87
+ croupier = Croupier.new( {
88
+ timeout: 0.5
89
+ } )
90
+
91
+ assert_raise BotError do
92
+ croupier.call( @bot, :return_after_sleep, [true, 10] )
93
+ end
94
+
95
+ end
96
+
97
+ should "disqualify bot that does not implement one of required methods" do
98
+
99
+ assert_raise BotError do
100
+ @croupier.call( @bot, :bet_one, [10, 100] )
101
+ end
102
+
103
+ end
104
+
105
+ should "disqualify bot that fails during respond" do
106
+
107
+ assert_raise BotError do
108
+ @croupier.call( @bot, :raise_exception )
109
+ end
110
+
111
+ end
112
+
113
+ should "disqualify bot for unacceptable response" do
114
+
115
+ assert_raise BotError do
116
+ @croupier.call( @bot, :return_after_sleep, [100, 0] ) do |bet|
117
+ bet < 50
118
+ end
119
+ end
120
+
121
+ end
122
+
123
+ should "be able to make parallel calls" do
124
+
125
+ croupier = Croupier.new( {
126
+ timeout: 1.01
127
+ } )
128
+
129
+ min_stake = 10
130
+ max_stake = 200
131
+
132
+ before = Time.now.to_i
133
+ bets = croupier.parallel_call( [@bot, @bot], :return_after_sleep, [min_stake, 1], [max_stake, 1] )
134
+ after = Time.now.to_i
135
+
136
+ assert_equal min_stake, bets[0]
137
+ assert_equal max_stake, bets[1]
138
+
139
+ assert_equal 1, after - before
140
+
141
+ end
142
+
143
+
144
+
145
+ end
146
+
147
+ end
@@ -0,0 +1,58 @@
1
+ require "test/unit"
2
+ require 'shoulda'
3
+ require "mocha/setup"
4
+
5
+ require "./lib/bitpoker"
6
+
7
+ class TestDuel < Test::Unit::TestCase
8
+
9
+ include BitPoker
10
+ include BitPoker::Rules
11
+
12
+ context "a duel" do
13
+
14
+ setup do
15
+ @bot = BotProxy.new( BotInterface.new )
16
+ @croupier = Croupier.new
17
+ @duel = Duel.new( @croupier, @bot, @bot )
18
+ end
19
+
20
+ should "have clear score table when started" do
21
+ assert_equal [0, 0], duel = @duel.total_score
22
+ end
23
+
24
+ should "know when finished" do
25
+
26
+ assert ! @duel.finished?
27
+
28
+ Round.any_instance.stubs( :state ).returns( Round::STATE_FINISHED )
29
+
30
+ ( 0 .. @croupier.rules[:rounds] ).each do
31
+ @duel.play_round
32
+ end
33
+
34
+ assert @duel.finished?
35
+
36
+ end
37
+
38
+ should "keep total score" do
39
+
40
+ Round.any_instance.stubs( :state ).returns( Round::STATE_FINISHED )
41
+ Round.any_instance.stubs( :score ).returns( [-100, 100] )
42
+
43
+ ( 0 .. @croupier.rules[:rounds] ).each do
44
+ @duel.play_round
45
+ end
46
+
47
+ total_score = [
48
+ -100 * @croupier.rules[:rounds],
49
+ 100 * @croupier.rules[:rounds]
50
+ ]
51
+
52
+ assert_equal total_score, @duel.total_score
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,272 @@
1
+ require "test/unit"
2
+ require 'shoulda'
3
+ require "mocha/setup"
4
+
5
+ require "./lib/bitpoker"
6
+ require_relative 'support/glass_bot'
7
+
8
+ class TestGameLogic < Test::Unit::TestCase
9
+
10
+ include BitPoker
11
+
12
+ context "a croupier that fallows game logic" do
13
+
14
+ setup do
15
+ @bot_one = GlassBot.new
16
+ @bot_two = GlassBot.new
17
+ @proxy_one = BotProxy.new( @bot_one )
18
+ @proxy_two = BotProxy.new( @bot_two )
19
+ @round = Round.new( [@proxy_one, @proxy_two] )
20
+ @croupier = Croupier.new
21
+ end
22
+
23
+ should "be able to perform rules introduction step" do
24
+
25
+ @round.state = Round::STATE_RULES_INTRODUCTION
26
+
27
+ @croupier.perform_next_step( @round )
28
+
29
+ # Test that all required rules are passed to bot
30
+ assert_equal @croupier.rules[:card_range].min, @bot_one.min_card
31
+ assert_equal @croupier.rules[:card_range].max, @bot_one.max_card
32
+ assert_equal @croupier.rules[:max_stake], @bot_one.max_stake
33
+ assert_equal @croupier.rules[:timeout], @bot_one.timeout
34
+
35
+ assert_equal Round::STATE_CARD_DEAL, @round.state
36
+
37
+ end
38
+
39
+ should "be able to perform card deal step" do
40
+
41
+ @round.state = Round::STATE_CARD_DEAL
42
+
43
+ @croupier.stubs( deal_cards: [4, 0] )
44
+ @proxy_one.stubs( :trigger ).with( :get_card, 4 ).returns( 4 )
45
+ @proxy_two.stubs( :trigger ).with( :get_card, 0 ).returns( 0 )
46
+
47
+ @croupier.perform_next_step( @round )
48
+
49
+ assert_equal [4, 0], @round.cards
50
+ assert_equal Round::STATE_FIRST_BETTING, @round.state
51
+
52
+ end
53
+
54
+ should "be able to perform first betting step when bets are not equal" do
55
+
56
+ @round.state = Round::STATE_FIRST_BETTING
57
+
58
+ @proxy_one.stubs( :trigger ).with( :bet_one, @croupier.rules[:min_stake] ).returns( 15 )
59
+ @proxy_two.stubs( :trigger ).with( :bet_one, @croupier.rules[:min_stake] ).returns( 25 )
60
+
61
+ @croupier.perform_next_step( @round )
62
+
63
+ assert_equal [15, 25], @round.bets
64
+ assert_equal Round::STATE_FIRST_CALL, @round.state
65
+
66
+ end
67
+
68
+ should "be able to perform first betting step when bets are equal" do
69
+
70
+ @round.state = Round::STATE_FIRST_BETTING
71
+
72
+ @proxy_one.stubs( :trigger ).with( :bet_one, @croupier.rules[:min_stake] ).returns( 25 )
73
+ @proxy_two.stubs( :trigger ).with( :bet_one, @croupier.rules[:min_stake] ).returns( 25 )
74
+
75
+ @croupier.perform_next_step( @round )
76
+
77
+ assert @round.bets_even?
78
+ assert_equal [25, 25], @round.bets
79
+ assert_equal Round::STATE_SECOND_BETTING, @round.state
80
+
81
+ end
82
+
83
+ should "be able to perform first call step when bot calls the stake" do
84
+
85
+ @round.state = Round::STATE_FIRST_CALL
86
+ @round.bets = [15, 25] # Now, proxy_one is lower bidder
87
+ assert_equal 25, @round.stake
88
+
89
+ @proxy_one.stubs( :trigger ).with( :agree_one, 25 ).returns( true )
90
+
91
+ @croupier.perform_next_step( @round )
92
+
93
+ assert_equal [15, 25], @round.bets
94
+ assert_equal Round::STATE_FIRST_CALLED, @round.state
95
+
96
+ end
97
+
98
+ should "be able to perform first call step when bot does not call the stake" do
99
+
100
+ @round.state = Round::STATE_FIRST_CALL
101
+ @round.bets = [15, 25] # Now, proxy_one is lower bidder
102
+ assert_equal 25, @round.stake
103
+
104
+ @proxy_one.stubs( :trigger ).with( :agree_one, 25 ).returns( false )
105
+
106
+ @croupier.perform_next_step( @round )
107
+
108
+ assert_equal [15, 25], @round.bets
109
+ assert_equal Round::STATE_FOLDED, @round.state
110
+
111
+ end
112
+
113
+ should "be able to perform first called step" do
114
+
115
+ @round.state = Round::STATE_FIRST_CALLED
116
+ @round.bets = [15, 25] # Now, proxy_one is lower bidder
117
+
118
+ @croupier.perform_next_step( @round )
119
+
120
+ assert @round.bets_even?
121
+ assert_equal [25, 25], @round.bets
122
+ assert_equal Round::STATE_SECOND_BETTING, @round.state
123
+
124
+ end
125
+
126
+ should "be able to perform folded step" do
127
+
128
+ @round.state = Round::STATE_FOLDED
129
+ @round.bets = [15, 25] # Now, proxy_one is folding player
130
+
131
+ @croupier.perform_next_step( @round )
132
+
133
+ assert_equal [-15, 15], @round.score
134
+ assert_equal Round::STATE_POINTS_DISTRIBUTION, @round.state
135
+
136
+ end
137
+
138
+ should "be able to perform second betting step when bets are already equal to max_stake" do
139
+
140
+ max_stake = @croupier.rules[:max_stake]
141
+
142
+ @round.state = Round::STATE_SECOND_BETTING
143
+ @round.bets = [max_stake, max_stake]
144
+
145
+ @croupier.perform_next_step( @round )
146
+
147
+ assert_equal Round::STATE_SHOWDOWN, @round.state
148
+
149
+ end
150
+
151
+ should "be able to perform second betting step when bets are equal" do
152
+
153
+ @round.bets = [25, 25]
154
+
155
+ @round.state = Round::STATE_SECOND_BETTING
156
+ @round.bets = [50, 50]
157
+
158
+ @proxy_one.stubs( :trigger ).with( :bet_two, @round.stake ).returns( 100 )
159
+ @proxy_two.stubs( :trigger ).with( :bet_two, @round.stake ).returns( 100 )
160
+
161
+ @croupier.perform_next_step( @round )
162
+
163
+ assert_equal [100, 100], @round.bets
164
+ assert @round.bets_even?
165
+ assert_equal Round::STATE_SHOWDOWN, @round.state
166
+
167
+ end
168
+
169
+ should "be able to perform second betting step when bets are not equal" do
170
+
171
+ @round.state = Round::STATE_SECOND_BETTING
172
+ @round.bets = [50, 50]
173
+
174
+ @proxy_one.stubs( :trigger ).with( :bet_two, @round.stake ).returns( 75 )
175
+ @proxy_two.stubs( :trigger ).with( :bet_two, @round.stake ).returns( 100 )
176
+
177
+ @croupier.perform_next_step( @round )
178
+
179
+ assert_equal @round.bets, [75, 100]
180
+ assert_equal Round::STATE_SECOND_CALL, @round.state
181
+
182
+ end
183
+
184
+ should "be able to perform second call step when bot calls the stake" do
185
+
186
+ @round.state = Round::STATE_SECOND_CALL
187
+ @round.bets = [75, 100] # Now, proxy_one is lower bidder
188
+
189
+ @proxy_one.stubs( :trigger ).with( :agree_two, 100 ).returns( true )
190
+
191
+ @croupier.perform_next_step( @round )
192
+
193
+ assert_equal [75, 100], @round.bets
194
+ assert_equal Round::STATE_SECOND_CALLED, @round.state
195
+
196
+ end
197
+
198
+ should "be able to perform second call step when bot does not call the stake" do
199
+
200
+ @round.state = Round::STATE_SECOND_CALL
201
+ @round.bets = [75, 100] # Now, proxy_one is lower bidder
202
+
203
+ @proxy_one.stubs( :trigger ).with( :agree_two, 100 ).returns( false )
204
+
205
+ @croupier.perform_next_step( @round )
206
+
207
+ assert_equal [75, 100], @round.bets
208
+ assert_equal Round::STATE_FOLDED, @round.state
209
+
210
+ end
211
+
212
+ should "be able to perform second called step" do
213
+
214
+ @round.state = Round::STATE_SECOND_CALLED
215
+ @round.bets = [75, 100] # Now, proxy_one is lower bidder
216
+
217
+ @croupier.perform_next_step( @round )
218
+
219
+ assert @round.bets_even?
220
+ assert_equal [100, 100], @round.bets
221
+ assert_equal Round::STATE_SHOWDOWN, @round.state
222
+
223
+ end
224
+
225
+ should "be able to perform showdown step when it is a draw" do
226
+
227
+ @round.state = Round::STATE_SHOWDOWN
228
+ @round.bets = [100, 100]
229
+ @round.cards = [4, 4] # It is the draw now
230
+
231
+ @croupier.perform_next_step( @round )
232
+
233
+ assert @round.bets_even?
234
+ assert @round.draw?
235
+ assert_equal [0, 0], @round.score
236
+ assert_equal Round::STATE_POINTS_DISTRIBUTION, @round.state
237
+
238
+ end
239
+
240
+ should "be able to perform showdown step when it is not a draw" do
241
+
242
+ @round.state = Round::STATE_SHOWDOWN
243
+ @round.bets = [100, 100]
244
+ @round.cards = [0, 4] # It is the draw now
245
+
246
+ @croupier.perform_next_step( @round )
247
+
248
+ assert @round.bets_even?
249
+ assert ! @round.draw?
250
+ assert_equal 100, @round.stake
251
+ assert_equal [-100, 100], @round.score
252
+ assert_equal Round::STATE_POINTS_DISTRIBUTION, @round.state
253
+
254
+ end
255
+
256
+ should "be able to perform points distribution step" do
257
+
258
+ @round.state = Round::STATE_POINTS_DISTRIBUTION
259
+ @round.score = [-100, 100]
260
+
261
+ @proxy_one.stubs( :trigger ).with( :end_of_round, -100 ).returns( -100 )
262
+ @proxy_two.stubs( :trigger ).with( :end_of_round, 100 ).returns( 100 )
263
+
264
+ @croupier.perform_next_step( @round )
265
+
266
+ assert_equal Round::STATE_FINISHED, @round.state
267
+
268
+ end
269
+
270
+ end
271
+
272
+ end