leonardo-bridge 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +46 -0
  6. data/Rakefile +1 -0
  7. data/bin/leo-console +7 -0
  8. data/bin/leo-play +143 -0
  9. data/bridge.gemspec +29 -0
  10. data/bridge.rc.rb +11 -0
  11. data/lib/bridge.rb +35 -0
  12. data/lib/bridge/auction.rb +182 -0
  13. data/lib/bridge/board.rb +84 -0
  14. data/lib/bridge/call.rb +98 -0
  15. data/lib/bridge/card.rb +54 -0
  16. data/lib/bridge/contract.rb +51 -0
  17. data/lib/bridge/db.rb +27 -0
  18. data/lib/bridge/deal.rb +33 -0
  19. data/lib/bridge/deck.rb +22 -0
  20. data/lib/bridge/game.rb +372 -0
  21. data/lib/bridge/hand.rb +25 -0
  22. data/lib/bridge/leonardo_result.rb +7 -0
  23. data/lib/bridge/player.rb +49 -0
  24. data/lib/bridge/result.rb +290 -0
  25. data/lib/bridge/trick.rb +28 -0
  26. data/lib/bridge/trick_play.rb +219 -0
  27. data/lib/bridge/version.rb +3 -0
  28. data/lib/enum.rb +32 -0
  29. data/lib/redis_model.rb +137 -0
  30. data/lib/uuid.rb +280 -0
  31. data/spec/auction_spec.rb +100 -0
  32. data/spec/board_spec.rb +19 -0
  33. data/spec/bridge_spec.rb +25 -0
  34. data/spec/call_spec.rb +44 -0
  35. data/spec/card_spec.rb +95 -0
  36. data/spec/db_spec.rb +19 -0
  37. data/spec/deck_spec.rb +14 -0
  38. data/spec/enum_spec.rb +14 -0
  39. data/spec/game_spec.rb +291 -0
  40. data/spec/hand_spec.rb +21 -0
  41. data/spec/player_spec.rb +22 -0
  42. data/spec/redis_model_spec.rb +50 -0
  43. data/spec/result_spec.rb +64 -0
  44. data/spec/spec_helper.rb +21 -0
  45. data/spec/support/auction_helper.rb +9 -0
  46. data/spec/support/test_enum.rb +5 -0
  47. data/spec/support/test_model.rb +3 -0
  48. data/spec/trick_play_spec.rb +90 -0
  49. data/spec/trick_spec.rb +15 -0
  50. metadata +240 -0
data/spec/hand_spec.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hand do
4
+ subject {
5
+ hand = Hand.new
6
+ hand.cards = Deck.new.first(12)
7
+ hand
8
+ }
9
+
10
+ it { subject.size.should eq(12) }
11
+
12
+ describe '#sort!' do
13
+ before {
14
+ subject.sort!
15
+ subject.sort! unless subject.cards.map {|c| c.suit}.uniq.size == 4
16
+ }
17
+
18
+ it { subject.cards.first.suit.should eq('C') }
19
+ it { subject.cards.last.suit.should eq('S') }
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Player do
4
+ let(:game) { Game.new }
5
+
6
+ context 'with one player' do
7
+ subject { game.add_player(Direction.north) }
8
+
9
+ it 'should not have a hand while in auction' do
10
+ expect { subject.hand }.to raise_error(GameError, 'Hand unknown')
11
+ end
12
+
13
+ it 'should not be able to start next game' do
14
+ expect { subject.start_next_game }.to raise_error(GameError, 'Not ready to start game')
15
+ end
16
+
17
+ it "should be able to start a game if available" do
18
+ subject.send(:game).stub(:next_game_ready?) { true }
19
+ subject.start_next_game.should eq(true)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisModel do
4
+ subject { RedisModel.new }
5
+
6
+ describe '#persist!' do
7
+ before { subject.persist! }
8
+ it { subject.id.should eq(1) }
9
+ it "should not alter existing id" do
10
+ subject.persist!
11
+ subject.id.should eq(1)
12
+ end
13
+ it { $redis.hgetall(subject.id).should be_a(Hash) }
14
+ end
15
+
16
+ describe '#load' do
17
+ before { subject.persist! }
18
+
19
+ it { RedisModel.load(subject.id).should be_a(RedisModel) }
20
+ it { RedisModel.load(subject.id).should eq(subject) }
21
+
22
+ it 'should have_timestamps disabled by default' do
23
+ expect { subject.created_at }.to raise_error
24
+ expect { subject.updated_at }.to raise_error
25
+ end
26
+ end
27
+
28
+ describe '#all' do
29
+ before { 5.times { RedisModel.new.persist! } }
30
+ subject { RedisModel }
31
+
32
+ it { subject.all.should be_a(Array) }
33
+ it { subject.all.size.should eq(5) }
34
+ end
35
+
36
+ describe 'descendant classes' do
37
+ subject { TestModel.new }
38
+
39
+ before { subject.persist! }
40
+
41
+ it 'should have_timestamps' do
42
+ expect { subject.created_at }.to_not raise_error
43
+ expect { subject.updated_at }.to_not raise_error
44
+ end
45
+ it { subject.created_at.should_not eq(nil) }
46
+ it { subject.updated_at.should_not eq(nil) }
47
+
48
+ it { TestModel.load(subject.id).id.should eq(subject.id) }
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Result do
4
+ let(:calls) {[
5
+ Pass.new(), Pass.new(), Bid.new(Level.one, Strain.club), Double.new(),
6
+ Redouble.new(), Pass.new(), Pass.new(), Bid.new(Level.one, Strain.no_trump),
7
+ Pass.new(), Bid.new(Level.two, Strain.heart), Pass.new(), Pass.new(),
8
+ Pass.new()
9
+ ]}
10
+
11
+ let(:game) {
12
+ # set up game objects
13
+ game = Game.new
14
+
15
+ board = Board.new(
16
+ :deal => Deal.new,
17
+ :dealer => Direction.north,
18
+ :vulnerability => Vulnerability.all
19
+ )
20
+
21
+ players = []
22
+ Direction.each do |position|
23
+ players[position] = game.add_player(position)
24
+ end
25
+
26
+ # perform contract
27
+ game.start!(board)
28
+ turn = board.dealer # Avoid calling getTurn.
29
+ calls.each do |c|
30
+ players[turn].make_call(c) # Each player passes.
31
+ turn = Direction[(turn+1) % Direction.size]
32
+ end
33
+
34
+ while not game.play.complete?
35
+ game.state.should eq(:playing)
36
+ turn = game.get_turn
37
+
38
+ # Find a valid card.
39
+ board.deal[turn].each do |card|
40
+ if game.play.valid_play?(card, turn, board.deal[turn])
41
+ if turn == game.play.dummy
42
+ players[game.play.declarer].play_card(card).should eq(true)
43
+ else
44
+ players[turn].play_card(card).should eq(true)
45
+ end
46
+ break
47
+ end
48
+ end
49
+ end
50
+ # Game complete, return it
51
+ game
52
+ }
53
+ subject { game.results.first }
54
+ xit { subject.is_major.should eq(true) }
55
+
56
+ describe 'score tests' do
57
+ let(:calls) {[Bid.new(Level.one, Strain.no_trump), Pass.new, Pass.new, Pass.new]}
58
+
59
+ it 'should score a 1NT+3 game correctly' do
60
+ r = DuplicateResult.new(game.board, game.contract, 10)
61
+ r.score.should eq(180)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,21 @@
1
+ #require 'simplecov'
2
+ #SimpleCov.start
3
+
4
+ require 'bridge'
5
+ root = File.dirname(__FILE__)
6
+
7
+ Dir[File.join(root,'support', '**','*.rb')].each { |f| require f }
8
+
9
+ RSpec.configure do |config|
10
+ config.color_enabled = true
11
+ config.mock_with :rspec
12
+ config.order = "random"
13
+
14
+ # set up test redis db
15
+ config.before(:suite) { $redis = Bridge::DB.new(:db => 15) }
16
+
17
+ # flush test redis db
18
+ config.after(:each) { $redis.flushdb }
19
+ end
20
+
21
+ include Bridge
@@ -0,0 +1,9 @@
1
+ module AuctionHelper
2
+ # expects an array of calls
3
+ def assert_current_calls(calls)
4
+ calls.map { |c| subject.make_call(c) unless c.nil? }
5
+ subject.current_bid.should eq(calls[0])
6
+ subject.current_double.should eq(calls[1])
7
+ subject.current_redouble.should eq(calls[2])
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module TestEnum
2
+ extend Enum
3
+
4
+ set_values :oh, :one, :two, :three
5
+ end
@@ -0,0 +1,3 @@
1
+ class TestModel < RedisModel
2
+ use_timestamps
3
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe TrickPlay do
4
+
5
+ subject { TrickPlay.new(Direction.east,Suit.club) }
6
+ let(:board) {
7
+ Board.new(
8
+ :deal => Deal.new,
9
+ :dealer => Direction.east,
10
+ :vulnerability => Vulnerability.all
11
+ )
12
+ }
13
+
14
+
15
+ it { subject.declarer.should eq(Direction.east) }
16
+ it { subject.lho.should eq(Direction.south) }
17
+ it { subject.rho.should eq(Direction.north) }
18
+ it { subject.dummy.should eq(Direction.west) }
19
+ it { subject.trumps.should eq(Suit.club) }
20
+ it { subject.played.size.should eq(4) }
21
+ it { subject.winners.size.should eq(0) }
22
+ it { subject.complete?.should eq(false) }
23
+ it { subject.whose_turn.should eq(Direction.south) }
24
+ it { subject.get_current_trick.cards.size.should eq(0) }
25
+
26
+ describe '#play_card' do
27
+ let(:card1) { Card.from_string('JC') }
28
+ let(:card2) { Card.from_string('2C') }
29
+ let(:card3) { Card.from_string('8C') }
30
+ let(:card4) { Card.from_string('5C') }
31
+ let(:card5) { Card.from_string('10H') }
32
+
33
+ before { subject.play_card(card1,nil) }
34
+
35
+ it { subject.get_trick_count.should eq([0,0]) }
36
+ it { subject.get_current_trick.cards.compact.size.should eq(1) }
37
+ xit { subject.get_current_trick.cards.size.should eq(3) }
38
+ it { subject.winning_card(subject.get_current_trick).should eq(false) }
39
+ it { subject.whose_turn.should eq(Direction.west) }
40
+ it { subject.played[Direction.south].size.should eq(1) }
41
+ it { subject.history.size.should eq(1) }
42
+ it { subject.who_played?(card1).should eq(Direction.south) }
43
+
44
+ describe '2 times' do
45
+ before { subject.play_card(card2,nil) }
46
+
47
+ it { subject.get_trick_count.should eq([0,0]) }
48
+ it { subject.get_current_trick.cards.compact.size.should eq(2) }
49
+ it { subject.get_current_trick.cards.size.should eq(4) }
50
+ it { subject.winning_card(subject.get_current_trick).should eq(false) }
51
+ it { subject.whose_turn.should eq(Direction.north) }
52
+ it { subject.played[Direction.west].size.should eq(1) }
53
+ it { subject.who_played?(card2).should eq(Direction.west) }
54
+
55
+ describe '3 times' do
56
+ before { subject.play_card(card3,nil) }
57
+
58
+ it { subject.get_trick_count.should eq([0,0]) }
59
+ it { subject.get_current_trick.cards.compact.size.should eq(3) }
60
+ it { subject.get_current_trick.cards.size.should eq(4) }
61
+ it { subject.winning_card(subject.get_current_trick).should eq(false) }
62
+ it { subject.whose_turn.should eq(Direction.east) }
63
+ it { subject.played[Direction.north].size.should eq(1) }
64
+ it { subject.who_played?(card3).should eq(Direction.north) }
65
+
66
+ describe '4 times' do
67
+ before { subject.play_card(card4,nil) }
68
+
69
+ it { subject.get_trick_count.should eq([0,1]) } # south won
70
+ it { subject.get_current_trick.cards.compact.size.should eq(4) }
71
+ it { subject.get_current_trick.cards.size.should eq(4) }
72
+ it { subject.winning_card(subject.get_current_trick).should eq(card1) }
73
+ it { subject.whose_turn.should eq(Direction.south) }
74
+ it { subject.played[Direction.east].size.should eq(1) }
75
+ it { subject.who_played?(card4).should eq(Direction.east) }
76
+
77
+ describe '5 times' do
78
+ before { subject.play_card(card5,nil) }
79
+ it { subject.get_trick_count.should eq([0,1]) } # south won
80
+ it { subject.get_current_trick.cards.compact.size.should eq(1) }
81
+ it { subject.winning_card(subject.get_current_trick).should eq(false) }
82
+ it { subject.whose_turn.should eq(Direction.west) }
83
+ it { subject.played[Direction.south].size.should eq(2) }
84
+ it { subject.who_played?(card5).should eq(Direction.south) }
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Trick do
4
+ subject { Trick.new }
5
+
6
+ it { subject.done?.should eq(false) }
7
+ it { subject.leader.should eq(nil) }
8
+ it { subject.cards.should eq([]) }
9
+
10
+ describe 'with 4 cards' do
11
+ before { Deck.new.first(4).each { |c| subject << c } }
12
+
13
+ it { subject.done?.should eq(true) }
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,240 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: leonardo-bridge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.3
5
+ platform: ruby
6
+ authors:
7
+ - Achilles Charmpilas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nutrun-string
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
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: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
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'
69
+ - !ruby/object:Gem::Dependency
70
+ name: fuubar
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: geminabox
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: gem-release
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: A lean mean bridge playing machine
140
+ email:
141
+ - ac@humbuckercode.co.uk
142
+ executables:
143
+ - leo-console
144
+ - leo-play
145
+ extensions: []
146
+ extra_rdoc_files: []
147
+ files:
148
+ - .gitignore
149
+ - Gemfile
150
+ - LICENSE.txt
151
+ - README.md
152
+ - Rakefile
153
+ - bin/leo-console
154
+ - bin/leo-play
155
+ - bridge.gemspec
156
+ - bridge.rc.rb
157
+ - lib/bridge.rb
158
+ - lib/bridge/auction.rb
159
+ - lib/bridge/board.rb
160
+ - lib/bridge/call.rb
161
+ - lib/bridge/card.rb
162
+ - lib/bridge/contract.rb
163
+ - lib/bridge/db.rb
164
+ - lib/bridge/deal.rb
165
+ - lib/bridge/deck.rb
166
+ - lib/bridge/game.rb
167
+ - lib/bridge/hand.rb
168
+ - lib/bridge/leonardo_result.rb
169
+ - lib/bridge/player.rb
170
+ - lib/bridge/result.rb
171
+ - lib/bridge/trick.rb
172
+ - lib/bridge/trick_play.rb
173
+ - lib/bridge/version.rb
174
+ - lib/enum.rb
175
+ - lib/redis_model.rb
176
+ - lib/uuid.rb
177
+ - spec/auction_spec.rb
178
+ - spec/board_spec.rb
179
+ - spec/bridge_spec.rb
180
+ - spec/call_spec.rb
181
+ - spec/card_spec.rb
182
+ - spec/db_spec.rb
183
+ - spec/deck_spec.rb
184
+ - spec/enum_spec.rb
185
+ - spec/game_spec.rb
186
+ - spec/hand_spec.rb
187
+ - spec/player_spec.rb
188
+ - spec/redis_model_spec.rb
189
+ - spec/result_spec.rb
190
+ - spec/spec_helper.rb
191
+ - spec/support/auction_helper.rb
192
+ - spec/support/test_enum.rb
193
+ - spec/support/test_model.rb
194
+ - spec/trick_play_spec.rb
195
+ - spec/trick_spec.rb
196
+ homepage: http://bridge.leonardogames.net/
197
+ licenses:
198
+ - MIT
199
+ metadata: {}
200
+ post_install_message:
201
+ rdoc_options: []
202
+ require_paths:
203
+ - lib
204
+ required_ruby_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ! '>='
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ required_rubygems_version: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ! '>='
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ requirements: []
215
+ rubyforge_project:
216
+ rubygems_version: 2.1.11
217
+ signing_key:
218
+ specification_version: 4
219
+ summary: Encapsulates all the necessary logic that allows 4 players to play a bridge
220
+ game. Also supports rubber scoring.
221
+ test_files:
222
+ - spec/auction_spec.rb
223
+ - spec/board_spec.rb
224
+ - spec/bridge_spec.rb
225
+ - spec/call_spec.rb
226
+ - spec/card_spec.rb
227
+ - spec/db_spec.rb
228
+ - spec/deck_spec.rb
229
+ - spec/enum_spec.rb
230
+ - spec/game_spec.rb
231
+ - spec/hand_spec.rb
232
+ - spec/player_spec.rb
233
+ - spec/redis_model_spec.rb
234
+ - spec/result_spec.rb
235
+ - spec/spec_helper.rb
236
+ - spec/support/auction_helper.rb
237
+ - spec/support/test_enum.rb
238
+ - spec/support/test_model.rb
239
+ - spec/trick_play_spec.rb
240
+ - spec/trick_spec.rb