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
@@ -0,0 +1,3 @@
1
+ module Bridge
2
+ VERSION = "0.4.3"
3
+ end
data/lib/enum.rb ADDED
@@ -0,0 +1,32 @@
1
+ # set values on each child like so:
2
+ # set_values :north, :east, :south, :west
3
+ module Enum
4
+ def set_values *args
5
+ self.define_singleton_method(:values) { args }
6
+ args.each_index do |i|
7
+ self.define_singleton_method(args[i]) { i }
8
+ end
9
+ end
10
+
11
+ def next index
12
+ next_index = index + 1
13
+ send(values[next_index].nil? ? values.first : values[next_index])
14
+ end
15
+
16
+ def [] index
17
+ self.send(values[index])
18
+ end
19
+
20
+ def name index
21
+ values[index].to_s
22
+ end
23
+
24
+ def all
25
+ values
26
+ end
27
+
28
+ def method_missing m, *args, &block
29
+ index_array = (0..(values.size-1)).to_a
30
+ index_array.respond_to?(m) ? index_array.send(m,*args,&block) : super
31
+ end
32
+ end
@@ -0,0 +1,137 @@
1
+ require File.join(File.dirname(__FILE__),'uuid')
2
+ require 'time'
3
+ require 'json'
4
+
5
+ # A basic Redis-backed Model implementation.
6
+ # Extend it by specifying your desired attr_accessors
7
+ # Enable timestamps by calling use_timestamps in your implementation
8
+ # Features auto-incremented ids and id namespacing
9
+ # Example:
10
+ # class Zorro < Fondue::RedisModel
11
+ # attr_accessor :sword, :mask
12
+ # use_timestamps
13
+ # end
14
+ #
15
+ # z = Zorro.new(:sword => 'rapier', :mask => 'lame')
16
+ # z.persist!
17
+ # z
18
+ # => #<Zorro @id="1", @sword => "rapier", @mask => "lame", @updated_at=2013-03-14 18:11:17 +0100, @created_at=2013-03-14 18:11:17 +0100>
19
+ #
20
+ class RedisModel
21
+ # the only default attribute
22
+ attr_accessor :id
23
+
24
+ def initialize params = {}
25
+ params.map do |k,v|
26
+ begin
27
+ send(:"#{k}=",JSON.parse(v))
28
+ rescue JSON::ParserError
29
+ send(:"#{k}=",v)
30
+ end
31
+ end
32
+
33
+ # typecast some standard attributes
34
+ self.id = self.id.to_i if self.id
35
+ if use_timestamps?
36
+ self.created_at = Time.parse(self.created_at) if self.created_at.is_a?(String)
37
+ self.updated_at = Time.parse(self.updated_at) if self.updated_at.is_a?(String)
38
+ end
39
+ end
40
+
41
+ def persist!
42
+ is_new = id.nil? or !self.class.exists?(id)
43
+
44
+ if use_timestamps?
45
+ self.updated_at = Time.now
46
+ self.created_at = Time.now if is_new
47
+ end
48
+
49
+ self.id = generate_id if is_new
50
+
51
+ $redis.hmset(namespaced_id, *self.to_array)
52
+ $redis.expire(namespaced_id,expires_in) unless expires_in.nil?
53
+ return true
54
+ end
55
+ alias :save! :persist!
56
+
57
+ def persist
58
+ persist!
59
+ self
60
+ end
61
+ alias :save :persist
62
+
63
+ def destroy!
64
+ $redis.del(namespaced_id)
65
+ end
66
+
67
+ def reload
68
+ self.class.load(namespaced_id)
69
+ end
70
+
71
+ def to_hash
72
+ instance_variables.inject({}) do |h,v|
73
+ value = instance_variable_get(v)
74
+ value = value.is_a?(String) ? value : value.to_json
75
+ h.update v.to_s.gsub('@','') => value
76
+ end
77
+ end
78
+
79
+ def use_timestamps?
80
+ self.class.instance_variable_get(:@use_timestamps)
81
+ end
82
+
83
+ def expires_in
84
+ self.class.instance_variable_get(:@expires_in)
85
+ end
86
+
87
+ def to_array
88
+ to_hash.to_a.flatten
89
+ end
90
+
91
+ def namespaced_id
92
+ self.class.namespaced_id(self.id)
93
+ end
94
+
95
+ # useful for testing, but also a general convenience
96
+ include Comparable
97
+ def <=>(other)
98
+ self.to_hash <=> other.to_hash
99
+ end
100
+
101
+ class << self
102
+ @use_timestamps = false
103
+
104
+ def namespaced_id(id)
105
+ "#{self}::#{id}"
106
+ end
107
+
108
+ def all
109
+ $redis.namespace(self)
110
+ end
111
+
112
+ def exists? id
113
+ $redis.exists(namespaced_id(id))
114
+ end
115
+
116
+ def load id
117
+ hash = $redis.hgetall(namespaced_id(id))
118
+ hash == {} ? nil : new(hash)
119
+ end
120
+ alias :find :load
121
+
122
+ def use_timestamps
123
+ @use_timestamps = true
124
+ self.send(:attr_accessor, :created_at)
125
+ self.send(:attr_accessor, :updated_at)
126
+ end
127
+
128
+ def expire_in time
129
+ @expires_in = time
130
+ end
131
+ end
132
+
133
+ protected
134
+ def generate_id
135
+ $redis.incr("_#{self.class}_current_id")
136
+ end
137
+ end
data/lib/uuid.rb ADDED
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env ruby
2
+ ### http://mput.dip.jp/mput/uuid.txt
3
+
4
+ # Copyright(c) 2005 URABE, Shyouhei.
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this code, to deal in the code without restriction, including without
8
+ # limitation the rights to use, copy, modify, merge, publish, distribute,
9
+ # sublicense, and/or sell copies of the code, and to permit persons to whom the
10
+ # code is furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the code.
14
+ #
15
+ # THE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE CODE OR THE USE OR OTHER DEALINGS IN THE
21
+ # CODE.
22
+ #
23
+ # 2009-02-20: Modified by Pablo Lorenzoni <pablo@propus.com.br> to correctly
24
+ # include the version in the raw_bytes.
25
+
26
+
27
+ require 'digest/md5'
28
+ require 'digest/sha1'
29
+ require 'tmpdir'
30
+
31
+ # Pure ruby UUID generator, which is compatible with RFC4122
32
+ UUID = Struct.new :raw_bytes
33
+
34
+ class UUID
35
+ private_class_method :new
36
+
37
+ class << self
38
+ def mask19 v, str # :nodoc
39
+ nstr = str.bytes.to_a
40
+ version = [0, 16, 32, 48, 64, 80][v]
41
+ nstr[6] &= 0b00001111
42
+ nstr[6] |= version
43
+ # nstr[7] &= 0b00001111
44
+ # nstr[7] |= 0b01010000
45
+ nstr[8] &= 0b00111111
46
+ nstr[8] |= 0b10000000
47
+ str = ''
48
+ nstr.each { |s| str << s.chr }
49
+ str
50
+ end
51
+
52
+ def mask18 v, str # :nodoc
53
+ version = [0, 16, 32, 48, 64, 80][v]
54
+ str[6] &= 0b00001111
55
+ str[6] |= version
56
+ # str[7] &= 0b00001111
57
+ # str[7] |= 0b01010000
58
+ str[8] &= 0b00111111
59
+ str[8] |= 0b10000000
60
+ str
61
+ end
62
+
63
+ def mask v, str
64
+ if RUBY_VERSION >= "1.9.0"
65
+ return mask19 v, str
66
+ else
67
+ return mask18 v, str
68
+ end
69
+ end
70
+ private :mask, :mask18, :mask19
71
+
72
+ # UUID generation using SHA1. Recommended over create_md5.
73
+ # Namespace object is another UUID, some of them are pre-defined below.
74
+ def create_sha1 str, namespace
75
+ sha1 = Digest::SHA1.new
76
+ sha1.update namespace.raw_bytes
77
+ sha1.update str
78
+ sum = sha1.digest
79
+ raw = mask 5, sum[0..15]
80
+ ret = new raw
81
+ ret.freeze
82
+ ret
83
+ end
84
+ alias :create_v5 :create_sha1
85
+
86
+ # UUID generation using MD5 (for backward compat.)
87
+ def create_md5 str, namespace
88
+ md5 = Digest::MD5.new
89
+ md5.update namespace.raw_bytes
90
+ md5.update str
91
+ sum = md5.digest
92
+ raw = mask 3, sum[0..16]
93
+ ret = new raw
94
+ ret.freeze
95
+ ret
96
+ end
97
+ alias :create_v3 :create_md5
98
+
99
+ # UUID generation using random-number generator. From it's random
100
+ # nature, there's no warranty that the created ID is really universaly
101
+ # unique.
102
+ def create_random
103
+ rnd = [
104
+ rand(0x100000000),
105
+ rand(0x100000000),
106
+ rand(0x100000000),
107
+ rand(0x100000000),
108
+ ].pack "N4"
109
+ raw = mask 4, rnd
110
+ ret = new raw
111
+ ret.freeze
112
+ ret
113
+ end
114
+ alias :create_v4 :create_random
115
+
116
+ def read_state fp # :nodoc:
117
+ fp.rewind
118
+ Marshal.load fp.read
119
+ end
120
+
121
+ def write_state fp, c, m # :nodoc:
122
+ fp.rewind
123
+ str = Marshal.dump [c, m]
124
+ fp.write str
125
+ end
126
+
127
+ private :read_state, :write_state
128
+ STATE_FILE = 'ruby-uuid'
129
+
130
+ # create the "version 1" UUID with current system clock, current UTC
131
+ # timestamp, and the IEEE 802 address (so-called MAC address).
132
+ #
133
+ # Speed notice: it's slow. It writes some data into hard drive on every
134
+ # invokation. If you want to speed this up, try remounting tmpdir with a
135
+ # memory based filesystem (such as tmpfs). STILL slow? then no way but
136
+ # rewrite it with c :)
137
+ def create clock=nil, time=nil, mac_addr=nil
138
+ c = t = m = nil
139
+ Dir.chdir Dir.tmpdir do
140
+ unless FileTest.exist? STATE_FILE then
141
+ # Generate a pseudo MAC address because we have no pure-ruby way
142
+ # to know the MAC address of the NIC this system uses. Note
143
+ # that cheating with pseudo arresses here is completely legal:
144
+ # see Section 4.5 of RFC4122 for details.
145
+ sha1 = Digest::SHA1.new
146
+ 256.times do
147
+ r = [rand(0x100000000)].pack "N"
148
+ sha1.update r
149
+ end
150
+ str = sha1.digest
151
+ r = rand 14 # 20-6
152
+ node = str[r, 6] || str
153
+ if RUBY_VERSION >= "1.9.0"
154
+ nnode = node.bytes.to_a
155
+ nnode[0] |= 0x01
156
+ node = ''
157
+ nnode.each { |s| node << s.chr }
158
+ else
159
+ node[0] |= 0x01 # multicast bit
160
+ end
161
+ k = rand 0x40000
162
+ open STATE_FILE, 'w' do |fp|
163
+ fp.flock IO::LOCK_EX
164
+ write_state fp, k, node
165
+ fp.chmod 0o777 # must be world writable
166
+ end
167
+ end
168
+ open STATE_FILE, 'r+' do |fp|
169
+ fp.flock IO::LOCK_EX
170
+ c, m = read_state fp
171
+ c = clock % 0x4000 if clock
172
+ m = mac_addr if mac_addr
173
+ t = time
174
+ if t.nil? then
175
+ # UUID epoch is 1582/Oct/15
176
+ tt = Time.now
177
+ t = tt.to_i*10000000 + tt.tv_usec*10 + 0x01B21DD213814000
178
+ end
179
+ c = c.succ # important; increment here
180
+ write_state fp, c, m
181
+ end
182
+ end
183
+
184
+ tl = t & 0xFFFF_FFFF
185
+ tm = t >> 32
186
+ tm = tm & 0xFFFF
187
+ th = t >> 48
188
+ th = th & 0x0FFF
189
+ th = th | 0x1000
190
+ cl = c & 0xFF
191
+ ch = c & 0x3F00
192
+ ch = ch >> 8
193
+ ch = ch | 0x80
194
+ pack tl, tm, th, cl, ch, m
195
+ end
196
+ alias :create_v1 :create
197
+
198
+ # A simple GUID parser: just ignores unknown characters and convert
199
+ # hexadecimal dump into 16-octet object.
200
+ def parse obj
201
+ str = obj.to_s.sub %r/\Aurn:uuid:/, ''
202
+ str.gsub! %r/[^0-9A-Fa-f]/, ''
203
+ raw = str[0..31].lines.to_a.pack 'H*'
204
+ ret = new raw
205
+ ret.freeze
206
+ ret
207
+ end
208
+
209
+ # The 'primitive constructor' of this class
210
+ # Note UUID.pack(uuid.unpack) == uuid
211
+ def pack tl, tm, th, ch, cl, n
212
+ raw = [tl, tm, th, ch, cl, n].pack "NnnCCa6"
213
+ ret = new raw
214
+ ret.freeze
215
+ ret
216
+ end
217
+ end
218
+
219
+ # The 'primitive deconstructor', or the dual to pack.
220
+ # Note UUID.pack(uuid.unpack) == uuid
221
+ def unpack
222
+ raw_bytes.unpack "NnnCCa6"
223
+ end
224
+
225
+ # Generate the string representation (a.k.a GUID) of this UUID
226
+ def to_s
227
+ a = unpack
228
+ tmp = a[-1].unpack 'C*'
229
+ a[-1] = sprintf '%02x%02x%02x%02x%02x%02x', *tmp
230
+ "%08x-%04x-%04x-%02x%02x-%s" % a
231
+ end
232
+ alias guid to_s
233
+
234
+ # Convert into a RFC4122-comforming URN representation
235
+ def to_uri
236
+ "urn:uuid:" + self.to_s
237
+ end
238
+ alias urn to_uri
239
+
240
+ # Convert into 128-bit unsigned integer
241
+ # Typically a Bignum instance, but can be a Fixnum.
242
+ def to_int
243
+ tmp = self.raw_bytes.unpack "C*"
244
+ tmp.inject do |r, i|
245
+ r * 256 | i
246
+ end
247
+ end
248
+ alias to_i to_int
249
+
250
+ # Gets the version of this UUID
251
+ # returns nil if bad version
252
+ def version
253
+ a = unpack
254
+ v = (a[2] & 0xF000).to_s(16)[0].chr.to_i
255
+ return v if (1..5).include? v
256
+ return nil
257
+ end
258
+
259
+ # Two UUIDs are said to be equal if and only if their (byte-order
260
+ # canonicalized) integer representations are equivallent. Refer RFC4122 for
261
+ # details.
262
+ def == other
263
+ to_i == other.to_i
264
+ end
265
+
266
+ include Comparable
267
+ # UUIDs are comparable (don't know what benefits are there, though).
268
+ def <=> other
269
+ to_s <=> other.to_s
270
+ end
271
+
272
+ # Pre-defined UUID Namespaces described in RFC4122 Appendix C.
273
+ NameSpace_DNS = parse "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
274
+ NameSpace_URL = parse "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
275
+ NameSpace_OID = parse "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
276
+ NameSpace_X500 = parse "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
277
+
278
+ # The Nil UUID in RFC4122 Section 4.1.7
279
+ Nil = parse "00000000-0000-0000-0000-000000000000"
280
+ end