bitpoker 0.1.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.
@@ -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