leonardo-bridge 0.4.3

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.
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