splendor_game 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 547dab96b270b9680b85697dcfdebf09fac4f8db
4
+ data.tar.gz: 9fbdd1e06feb465f45e47b43018fc0b497a913b0
5
+ SHA512:
6
+ metadata.gz: e63c1ad7284bafc6844d171967b9eeada48d1cc973b0de224531e31a4a9981f14cf22db2d06a16bcc36f190304440b840bdd1b437952432d2fa368b61f8c29a7
7
+ data.tar.gz: 2675112afc9482cb891145bb3a88e32ef96dfe26de643d4ecaba1fd1cb179ae6e3163fbaf9c1091cdf8524268baa23b95d58110234797d2dbe37f74b00e761e1
data/.codeclimate.yml ADDED
@@ -0,0 +1,27 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: true
5
+ config:
6
+ languages:
7
+ ruby:
8
+ mass_threshold: 22
9
+ javascript:
10
+ python:
11
+ php:
12
+ fixme:
13
+ enabled: true
14
+ rubocop:
15
+ enabled: true
16
+ ratings:
17
+ paths:
18
+ - "**.inc"
19
+ - "**.js"
20
+ - "**.jsx"
21
+ - "**.module"
22
+ - "**.php"
23
+ - "**.py"
24
+ - "**.rb"
25
+ exclude_paths:
26
+ - spec/
27
+ - lib/splendor_game/load_cards.rb
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.5
5
+ before_install: gem install bundler -v 1.13.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in splendor_game.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 reedstonefood
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE 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
+ AUTHORS OR COPYRIGHT HOLDERS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Splendor_game
2
+
3
+ [![Code Climate](https://codeclimate.com/github/reedstonefood/splendor_game/badges/gpa.svg)](https://codeclimate.com/github/reedstonefood/splendor_game)
4
+
5
+ A Ruby implementation of the board game Splendor.
6
+
7
+ Find out more about the game at [BoardGameGeek](https://boardgamegeek.com/boardgame/148228/splendor).
8
+
9
+ ## Installation
10
+
11
+ Not yet ready for use - still in development.
12
+
13
+ ## Usage
14
+
15
+ There is a simple CLI you can use. Simply run the following in irb or put in a .rb file and run it.
16
+
17
+ ```ruby
18
+ require 'splendor_game'
19
+ SplendorGame::CLI.new
20
+ ```
21
+
22
+ Or, hook this up to any front end you might desire to use.
23
+
24
+ ## Contributing
25
+
26
+ Bug reports and pull requests are welcome on GitHub at https://github.com/reedstonefood/splendor_game.
27
+
28
+ ## With thanks...
29
+
30
+ Whoever typed up a list of all the cards in the board game into this spreadsheet... https://drive.google.com/file/d/0B4yyYVH10iE5VlBFME9QelBVUnc/edit
31
+
32
+ ## License
33
+
34
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
35
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "splendor_game"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,23 @@
1
+ require "splendor_game/version"
2
+ require "splendor_game/options"
3
+ require "splendor_game/coloured_object"
4
+ require "splendor_game/card"
5
+ require "splendor_game/noble"
6
+ require "splendor_game/tableau"
7
+ require "splendor_game/player"
8
+ require "splendor_game/turn"
9
+ require "splendor_game/load_cards"
10
+ require "splendor_game/game"
11
+ require "splendor_game/cli"
12
+
13
+ module SplendorGame
14
+ # This is to validate when loading cards
15
+ VALID_COLOUR_LIST = ["RED", "BLUE", "BLACK", "GREEN", "WHITE"]
16
+ # This is used to validate certain calls
17
+ VALID_COLOUR_SYMBOLS = VALID_COLOUR_LIST.map { |x| x.downcase.to_sym }
18
+ # Note gold is a special colour so shouldn't be in VALID_COLOUR_LIST
19
+ VALID_COLOUR_SYMBOLS << :gold
20
+
21
+ MAX_PLAYER_COUNT = 4
22
+ MIN_PLAYER_COUNT = 2
23
+ end
@@ -0,0 +1,24 @@
1
+ module SplendorGame
2
+
3
+ class Card < ColouredObject
4
+ attr_reader :level, :points, :colour, :cost
5
+
6
+ def initialize(level, colour, cost, points = 0)
7
+ @level = level
8
+ @points = points
9
+ @colour = colour
10
+ @cost, @cost_error = Hash.new(), Hash.new()
11
+ # if the colour is valid, load it, if not, put it in an error hash
12
+ cost.each do |key, value|
13
+ new_key_name = validate_colour(key)
14
+ if new_key_name==false
15
+ @cost_error[key] = value
16
+ else
17
+ @cost[new_key_name] = value
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,255 @@
1
+ require 'highline'
2
+
3
+ module SplendorGame
4
+
5
+ class CLI
6
+ @@cli = HighLine.new
7
+ attr_reader :level, :points, :colour, :cost
8
+
9
+ def initialize
10
+ @g = SplendorGame::Game.new Hash[*ARGV]
11
+ choose_players
12
+ main
13
+ end
14
+
15
+ def choose_players
16
+ count = 1
17
+ @@cli.say "Name all the players. Input 'done' when you are done."
18
+ loop do
19
+ pname = @@cli.ask("Enter name of player #{count} > ") do |q|
20
+ q.validate = lambda { |a| a.length >= 1 && a.length <= 19 }
21
+ end
22
+ if pname.downcase == 'done'
23
+ break if count > MIN_PLAYER_COUNT
24
+ @@cli.say "You need to input at least 2 players!"
25
+ elsif @g.add_player(pname)== true
26
+ count += 1
27
+ @@cli.say "*** #{pname} successfully added"
28
+ else
29
+ @@cli.say "*** Sorry, there was a problem adding player #{pname}"
30
+ end
31
+ break if count > MAX_PLAYER_COUNT
32
+ end
33
+ @@cli.say "Succesfully added #{count-1} players. Game is ready to start."
34
+ end
35
+
36
+ def puts_help
37
+ @@cli.say "************************ HELP! ************************"
38
+ @@cli.say "<%= color('(b)uy', BOLD) %> = Buy a card"
39
+ @@cli.say "<%= color('(r)eserve', BOLD) %> = Reserve a card"
40
+ @@cli.say "<%= color('(t)okens', BOLD) %> = Pick up tokens from the bank"
41
+ @@cli.say "<%= color('(n)obles', BOLD) %> = Look at the available nobles"
42
+ @@cli.say "<%= color('(h)elp', BOLD) %> = This help page"
43
+ @@cli.say "<%= color('e(x)it', BOLD) %> = Exit the program"
44
+ end
45
+
46
+ def full_display
47
+ @g.display.each do |row, deck|
48
+ @@cli.say "ROW #{row}"
49
+ deck.each do |card|
50
+ @@cli.say card_display(card)
51
+ end
52
+ end
53
+ end
54
+
55
+ #practicing using args rather than fixed list of parameters
56
+ def purchase_card(args)
57
+ #if args[:turn].player.tableau.reserved_cards.include?(args[:card])
58
+ # args[:turn].reserve_card
59
+ if args[:turn].purchase_card(args[:card])
60
+ true
61
+ else
62
+ @@cli.say "Oops, you can't afford that"
63
+ end
64
+ end
65
+
66
+ def reserve_card(args)
67
+ if args[:card].is_a?(SplendorGame::Card) && args[:turn].reserve_displayed_card(args[:card])
68
+ true
69
+ elsif args[:card].is_a?(Integer) && args[:turn].reserve_random_card(args[:card])
70
+ true
71
+ else
72
+ @@cli.say "Sorry, you can't reserve that (maybe you have reserved too many cards)"
73
+ end
74
+ end
75
+
76
+ def do_turn(turn)
77
+ while turn.action_done == false
78
+ output_all_player_details(turn.player)
79
+ input = @@cli.ask "What do you want to do, <%= color('#{turn.player.name}', BOLD) %>? "
80
+ command_result = process_command(input.downcase, turn)
81
+ if !command_result
82
+ @@cli.say "Sorry, I did not understand that. Press h for help"
83
+ elsif command_result==:exit
84
+ break
85
+ end
86
+ end
87
+ consider_nobles(turn)
88
+ turn.end_turn
89
+ @@cli.say "*** END OF TURN***"
90
+ command_result==:exit ? false : true
91
+ end
92
+
93
+ def process_command(input, turn)
94
+ case
95
+ when input[0]=='b'
96
+ card = choose_card(:buy, turn.player)
97
+ purchase_card(:card => card, :turn => turn) if card
98
+ when input[0]=='r'
99
+ card = choose_card(:reserve, turn.player)
100
+ reserve_card(:card => card, :turn => turn) if card
101
+ when input[0]=='h'
102
+ puts_help
103
+ when input[0]=='n'
104
+ display_nobles
105
+ when input[0]=='t'
106
+ @@cli.say bank_details + " "
107
+ take_tokens(turn)
108
+ when input[0]=='x'
109
+ return :exit
110
+ else
111
+ return false
112
+ end
113
+ true
114
+ end
115
+
116
+
117
+ def card_display(card)
118
+ text = "#{card.points}pts "
119
+ text << "(#{card.colour}) => " if card.instance_variable_defined?(:@colour)
120
+ card.cost.each do |k,v|
121
+ text << "#{v} x #{k}, "
122
+ end
123
+ text[0..-3]
124
+ end
125
+
126
+ def output_all_player_details(highlighted_player=nil)
127
+ @g.players.each do |p|
128
+ if p==highlighted_player
129
+ str = "<%= color('"
130
+ str << player_details(p)
131
+ str << "', BOLD) %>"
132
+ else
133
+ str = player_details(p)
134
+ end
135
+ @@cli.say str
136
+ end
137
+ end
138
+
139
+ def player_details(player)
140
+ str = "#{player.name.ljust(19)}: #{player.points.to_s.ljust(2)}pts. "
141
+ reserved_card_count = player.tableau.reserved_cards.count
142
+ str << "(#{reserved_card_count}R) " if reserved_card_count > 0
143
+ str << "Cards (#{player.tableau.cards.count}): "
144
+ player.tableau.all_colours_on_cards.sort.to_h.each do |colour, count|
145
+ str << "#{colour}=#{count} " if count > 0
146
+ end
147
+ str << "Tokens: "
148
+ player.tableau.tokens.sort.to_h.each do |colour,count|
149
+ str << "#{colour}=#{count} " if count > 0
150
+ end
151
+ str[0..-2]
152
+ end
153
+
154
+ def bank_details
155
+ str = "Bank tokens = "
156
+ @g.bank.tokens.sort.to_h.each do |colour, count|
157
+ str << "#{colour}=#{count} " if count > 0
158
+ end
159
+ str
160
+ end
161
+
162
+ def choose_card(mode, player = nil)
163
+ displayed_cards_list = @g.all_displayed_cards.collect { |c| [card_display(c),c] }.to_h
164
+ if mode==:reserve
165
+ (1..3).each { |i| displayed_cards_list["Reserve mystery level #{i} card"]= i }
166
+ end
167
+ if mode==:buy
168
+ player.tableau.reserved_cards.each_with_index do |card, index|
169
+ displayed_cards_list["R#{index+1} - #{card_display(card)}"]= card
170
+ end
171
+ end
172
+ @@cli.choose do |menu|
173
+ menu.prompt = "Which card do you want to #{mode}? "
174
+ menu.choices(*displayed_cards_list.keys) do |chosen|
175
+ @@cli.say "Nice, you chose #{chosen}."
176
+ displayed_cards_list[chosen]
177
+ end
178
+ menu.choice(:cancel) { return false }
179
+ end
180
+ end
181
+
182
+ #def validate_token_choice(t)
183
+ # return false if [2,3].include?(t.count)
184
+ # t.each { |c| return false if !VALID_COLOUR_SYMBOLS.include?(c.upcase) || c==:gold}
185
+ # return false if t.count==2 && t[0] != t[1]
186
+ # true
187
+ #end
188
+
189
+ def take_tokens(turn)
190
+ input = @@cli.ask "Which tokens would you like (CSV format)? "
191
+ requested_tokens = input.split(",")
192
+ #return false if !validate_token_choice(requested_tokens)
193
+ if requested_tokens.count==2
194
+ response = turn.take_two_tokens_same_colour(requested_tokens[0])
195
+ elsif requested_tokens.count==3
196
+ response = turn.take_different_tokens(requested_tokens)
197
+ end
198
+ @@cli.say "Oops, that's not a valid selection" if !response
199
+ end
200
+
201
+ def consider_nobles(turn)
202
+ possibles = turn.claimable_nobles
203
+ return false if possibles.empty?
204
+ return assign_only_valid_noble(turn, possibles) if possibles.count==1
205
+ displayed_nobles_list = possibles.collect { |c| [card_display(c),c] }.to_h
206
+ @@cli.choose do |menu|
207
+ menu.prompt = "You qualify for multiple nobles! Pick one... "
208
+ menu.choices(*displayed_nobles_list.keys) do |chosen|
209
+ turn.claim_noble(displayed_nobles_list[chosen])
210
+ @@cli.say "Nice, you chose #{chosen}."
211
+ end
212
+ end
213
+
214
+ end
215
+
216
+ def display_nobles
217
+ @g.nobles.each do |noble|
218
+ @@cli.say "NOBLE - #{card_display(noble)}"
219
+ end
220
+ end
221
+
222
+ def end_game_detail
223
+ @@cli.say "The game consisted of #{@g.turns.count} turns"
224
+ @@cli.ask "It's the end of the game. Press enter to end the program."
225
+ @@cli.say "Goodbye!"
226
+ end
227
+
228
+
229
+ def main
230
+ @g.start_game
231
+ catch :exit do
232
+ loop do
233
+ turn = @g.next_turn
234
+ throw :exit if turn===false # or @exit_flag==true ??
235
+ throw :exit if !do_turn(turn)
236
+ end #end of the game - only reachable by throwing an :exit
237
+ end
238
+ end_game_detail
239
+ end
240
+
241
+ private
242
+ def assign_only_valid_noble(turn,noble_array)
243
+ the_noble = noble_array.first
244
+ if !turn.claim_noble(the_noble)
245
+ @@cli.say "Looks like you qualify for a noble, but it didn't work"
246
+ return false
247
+ else
248
+ @@cli.say "You have been given a noble! #{the_noble.points} points"
249
+ return true
250
+ end
251
+ end
252
+
253
+ end
254
+
255
+ end
@@ -0,0 +1,17 @@
1
+ module SplendorGame
2
+
3
+ class ColouredObject
4
+ private
5
+ # format any inputted colours to the downcase symbol version
6
+ # return false if an invalid input is passed in
7
+ def validate_colour(input)
8
+ if VALID_COLOUR_LIST.include?(input.to_s.upcase)
9
+ input.downcase.to_sym
10
+ else
11
+ false
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,64 @@
1
+ class Object
2
+ def is_natural_number?
3
+ return false if !self.is_a?(Numeric)
4
+ return false if self < 0
5
+ return false if self != self.abs
6
+ true
7
+ end
8
+ end
9
+
10
+ module SplendorGame
11
+
12
+
13
+ class Game
14
+ attr_reader :deck, :bank, :players, :nobles, :options, :display, :turns
15
+ def initialize(user_options = nil)
16
+ @options = Options.new(user_options).give_options
17
+ load_cards # puts all the cards into shuffled decks... a hash of arrays of cards. @deck[level]
18
+ @bank = Tableau.new(0) # 0 means no limit on token capacity
19
+ @players = Array.new()
20
+ @turns = Array.new()
21
+ end
22
+
23
+ def add_player(player_name)
24
+ return false if @players.count >= MAX_PLAYER_COUNT
25
+ @players << Player.new(player_name, @players.count+1,@options[:player_token_limit])
26
+ true
27
+ end
28
+
29
+ def next_player
30
+ if !@turns.empty?
31
+ @players.rotate!
32
+ return @players.first
33
+ end
34
+ @players.shuffle!
35
+ @starting_player = @players.first
36
+ end
37
+
38
+ def game_over?
39
+ return false if @players.map { |p| p.points }.max < @options[:winning_score]
40
+ return true if @players[1] == @starting_player
41
+ false
42
+ end
43
+
44
+ def start_game
45
+ @bank.seed_bank({:options=> @options, :player_count => @players.count})
46
+ @nobles = noble_sample(@options[:nobles_available][@players.count])
47
+ @display = Hash.new()
48
+ @deck.each { |level, subdeck| @display[level] = subdeck.pop(@options[:display_cards_per_row]) }
49
+ end
50
+
51
+ def next_turn
52
+ return false if game_over? || !defined? @display
53
+ t = SplendorGame::Turn.new(self, next_player)
54
+ @turns << t
55
+ t
56
+ end
57
+
58
+ def all_displayed_cards
59
+ @display.values.flatten
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,126 @@
1
+ module SplendorGame
2
+ class Game
3
+ #Could read from an external file eventually, but this works just fine :)
4
+ def load_cards
5
+ all_cards = Array.new()
6
+ all_cards << Card.new(1, :white, {:blue => 3})
7
+ all_cards << Card.new(1, :white, {:red => 2, :black => 1})
8
+ all_cards << Card.new(1, :white, {:blue => 1, :green => 1, :red => 1, :black => 1})
9
+ all_cards << Card.new(1, :white, {:blue => 2, :black => 2})
10
+ all_cards << Card.new(1, :white, {:green => 4}, 1)
11
+ all_cards << Card.new(1, :white, {:blue => 1, :green => 2, :red => 1, :black => 1})
12
+ all_cards << Card.new(1, :white, {:blue => 2, :green => 2, :black => 1})
13
+ all_cards << Card.new(1, :white, {:white => 3, :blue => 1, :black => 1})
14
+ all_cards << Card.new(1, :blue, {:white => 1, :black => 2})
15
+ all_cards << Card.new(1, :blue, {:black => 3})
16
+ all_cards << Card.new(1, :blue, {:white => 1, :green => 1, :red => 1, :black => 1})
17
+ all_cards << Card.new(1, :blue, {:green => 2, :black => 2})
18
+ all_cards << Card.new(1, :blue, {:red => 4}, 1)
19
+ all_cards << Card.new(1, :blue, {:white => 1, :green => 1, :red => 2, :black => 1})
20
+ all_cards << Card.new(1, :blue, {:white => 1, :green => 2, :red => 2})
21
+ all_cards << Card.new(1, :blue, {:blue => 1, :green => 3, :red => 1})
22
+ all_cards << Card.new(1, :green, {:white => 2, :blue => 1})
23
+ all_cards << Card.new(1, :green, {:red => 3})
24
+ all_cards << Card.new(1, :green, {:white => 1, :blue => 1, :red => 1, :black => 1})
25
+ all_cards << Card.new(1, :green, {:blue => 2, :red => 2})
26
+ all_cards << Card.new(1, :green, {:black => 4}, 1)
27
+ all_cards << Card.new(1, :green, {:white => 1, :blue => 1, :red => 1, :black => 2})
28
+ all_cards << Card.new(1, :green, {:blue => 1, :red => 2, :black => 2})
29
+ all_cards << Card.new(1, :green, {:white => 1, :blue => 3, :green => 1})
30
+ all_cards << Card.new(1, :red, {:blue => 2, :green => 1})
31
+ all_cards << Card.new(1, :red, {:white => 3})
32
+ all_cards << Card.new(1, :red, {:white => 1, :blue => 1, :green => 1, :black => 1})
33
+ all_cards << Card.new(1, :red, {:white => 2, :red => 2})
34
+ all_cards << Card.new(1, :red, {:white => 4}, 1)
35
+ all_cards << Card.new(1, :red, {:white => 2, :blue => 1, :green => 1, :black => 1})
36
+ all_cards << Card.new(1, :red, {:white => 2, :green => 1, :black => 2})
37
+ all_cards << Card.new(1, :red, {:white => 1, :red => 1, :black => 3})
38
+ all_cards << Card.new(1, :black, {:green => 2, :red => 1})
39
+ all_cards << Card.new(1, :black, {:green => 3})
40
+ all_cards << Card.new(1, :black, {:white => 1, :blue => 1, :green => 1, :red => 1})
41
+ all_cards << Card.new(1, :black, {:white => 2, :green => 2})
42
+ all_cards << Card.new(1, :black, {:blue => 4}, 1)
43
+ all_cards << Card.new(1, :black, {:white => 1, :blue => 2, :green => 1, :red => 1})
44
+ all_cards << Card.new(1, :black, {:white => 2, :blue => 2, :red => 1})
45
+ all_cards << Card.new(1, :black, {:green => 1, :red => 3, :black => 1})
46
+
47
+ all_cards << Card.new(2, :white, {:red => 5}, 2)
48
+ all_cards << Card.new(2, :white, {:white => 6}, 3)
49
+ all_cards << Card.new(2, :white, {:green => 3, :red => 2, :black => 2}, 1)
50
+ all_cards << Card.new(2, :white, {:green => 1, :red => 4, :black => 2}, 2)
51
+ all_cards << Card.new(2, :white, {:white => 2, :blue => 3, :red => 3}, 1)
52
+ all_cards << Card.new(2, :white, {:red => 5, :black => 3}, 2)
53
+ all_cards << Card.new(2, :blue, {:blue => 5}, 2)
54
+ all_cards << Card.new(2, :blue, {:blue => 6}, 3)
55
+ all_cards << Card.new(2, :blue, {:blue => 2, :green => 2, :red => 3}, 1)
56
+ all_cards << Card.new(2, :blue, {:white => 2, :red => 1, :black => 4}, 2)
57
+ all_cards << Card.new(2, :blue, {:blue => 2, :green => 3, :black => 3}, 1)
58
+ all_cards << Card.new(2, :blue, {:white => 5, :blue => 3}, 2)
59
+ all_cards << Card.new(2, :green, {:green => 5}, 2)
60
+ all_cards << Card.new(2, :green, {:green => 6}, 3)
61
+ all_cards << Card.new(2, :green, {:white => 2, :blue => 3, :black => 2}, 1)
62
+ all_cards << Card.new(2, :green, {:white => 3, :green => 2, :red => 3}, 1)
63
+ all_cards << Card.new(2, :green, {:white => 4, :blue => 2, :black => 1}, 2)
64
+ all_cards << Card.new(2, :green, {:blue => 5, :green => 3}, 2)
65
+ all_cards << Card.new(2, :red, {:black => 5}, 2)
66
+ all_cards << Card.new(2, :red, {:red => 6}, 3)
67
+ all_cards << Card.new(2, :red, {:white => 2, :red => 2, :black => 3}, 1)
68
+ all_cards << Card.new(2, :red, {:white => 1, :blue => 4, :green => 2}, 2)
69
+ all_cards << Card.new(2, :red, {:blue => 3, :red => 2, :black => 3}, 1)
70
+ all_cards << Card.new(2, :red, {:white => 3, :black => 5}, 2)
71
+ all_cards << Card.new(2, :black, {:white => 5}, 2)
72
+ all_cards << Card.new(2, :black, {:black => 6}, 3)
73
+ all_cards << Card.new(2, :black, {:white => 3, :blue => 2, :green => 2}, 1)
74
+ all_cards << Card.new(2, :black, {:blue => 1, :green => 4, :red => 2}, 2)
75
+ all_cards << Card.new(2, :black, {:white => 3, :green => 3, :black => 2}, 1)
76
+ all_cards << Card.new(2, :black, {:green => 5, :red => 3}, 2)
77
+
78
+ all_cards << Card.new(3, :white, {:black => 7}, 4)
79
+ all_cards << Card.new(3, :white, {:white => 3, :black => 7}, 5)
80
+ all_cards << Card.new(3, :white, {:white => 3, :red => 3, :black => 6}, 4)
81
+ all_cards << Card.new(3, :white, {:blue => 3, :green => 3, :red => 5, :black => 3}, 3)
82
+ all_cards << Card.new(3, :blue, {:white => 7}, 4)
83
+ all_cards << Card.new(3, :blue, {:white => 7, :blue => 3}, 5)
84
+ all_cards << Card.new(3, :blue, {:white => 6, :blue => 3, :black => 3}, 4)
85
+ all_cards << Card.new(3, :blue, {:white => 3, :green => 3, :red => 3, :black => 5}, 3)
86
+ all_cards << Card.new(3, :green, {:blue => 7}, 4)
87
+ all_cards << Card.new(3, :green, {:blue => 7, :green => 3}, 5)
88
+ all_cards << Card.new(3, :green, {:white => 3, :blue => 6, :green => 3}, 4)
89
+ all_cards << Card.new(3, :green, {:white => 5, :blue => 3, :red => 3, :black => 3}, 3)
90
+ all_cards << Card.new(3, :red, {:green => 7}, 4)
91
+ all_cards << Card.new(3, :red, {:green => 7, :red => 3}, 5)
92
+ all_cards << Card.new(3, :red, {:blue => 3, :green => 6, :red => 3}, 4)
93
+ all_cards << Card.new(3, :red, {:white => 3, :blue => 5, :green => 3, :black => 3}, 3)
94
+ all_cards << Card.new(3, :black, {:red => 7}, 4)
95
+ all_cards << Card.new(3, :black, {:red => 7, :black => 3}, 5)
96
+ all_cards << Card.new(3, :black, {:green => 3, :red => 6, :black => 3}, 4)
97
+ all_cards << Card.new(3, :black, {:white => 3, :blue => 3, :green => 5, :red => 3}, 3)
98
+
99
+
100
+
101
+ # Now all cards are in all_cards, distribute them into all_cards
102
+ @deck = Hash.new()
103
+ all_cards.each do |card|
104
+ @deck[card.level] = Array.new() if !@deck.include?(card.level)
105
+ @deck[card.level] << card
106
+ end
107
+ # shuffle them
108
+ @deck.each { |_deck_num, smaller_deck| smaller_deck.shuffle! }
109
+ end
110
+
111
+ def noble_sample(number_of_cards_to_load)
112
+ all_nobles = Array.new()
113
+ all_nobles << Noble.new({:white => 3, :blue => 3, :black => 3}, 3)
114
+ all_nobles << Noble.new({:blue => 3, :green => 3, :red => 3}, 3)
115
+ all_nobles << Noble.new({:white => 3, :red => 3, :black => 3}, 3)
116
+ all_nobles << Noble.new({:white => 3, :blue => 3, :green => 3}, 3)
117
+ all_nobles << Noble.new({:green => 3, :red => 3, :black => 3}, 3)
118
+ all_nobles << Noble.new({:green => 4, :red => 4}, 3)
119
+ all_nobles << Noble.new({:blue => 4, :green => 4}, 3)
120
+ all_nobles << Noble.new({:red => 4, :black => 4}, 3)
121
+ all_nobles << Noble.new({:white => 4, :black => 4}, 3)
122
+ all_nobles << Noble.new({:white => 4, :blue => 4}, 3)
123
+ all_nobles.shuffle!.first(number_of_cards_to_load)
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,22 @@
1
+ module SplendorGame
2
+
3
+ #Players become eligible for Nobles when they have cards that meet the cost (NOT tokens)
4
+ class Noble < ColouredObject
5
+ attr_reader :cost, :points
6
+
7
+ def initialize(cost, points = 0)
8
+ @points = points
9
+ @cost, @cost_error = Hash.new(), Hash.new()
10
+ # if the colour is valid, load it, if not, put it in an error hash
11
+ cost.each do |key, value|
12
+ new_key_name = validate_colour(key)
13
+ if new_key_name==false
14
+ @cost_error[key] = value
15
+ else
16
+ @cost[new_key_name] = value
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,48 @@
1
+ module SplendorGame
2
+
3
+ class Options
4
+ NOBLES_AVAILABLE = { 2 => 3, 3 => 4, 4 => 5}
5
+ STARTING_GOLD_TOKENS = 5
6
+ STARTING_NON_GOLD_TOKENS = { 2 => 4, 3 => 5, 4 => 7}
7
+ DISPLAY_CARDS_PER_ROW = 4
8
+ WINNING_SCORE = 15
9
+ MIN_TO_TAKE_TWO = 4
10
+ PLAYER_TOKEN_LIMIT = 10
11
+ attr_reader :deck, :bank, :players, :nobles, :options, :display
12
+ def initialize(user_options = nil)
13
+ if user_options.is_a?(Hash)
14
+ @user_options = user_options
15
+ else
16
+ @user_options = Hash.new()
17
+ end
18
+ end
19
+
20
+ def clean_user_options
21
+ [:starting_non_gold_tokens, :nobles_available].each do |key|
22
+ if @user_options[key].respond_to?(:keys)
23
+ @user_options.delete(key) if !([2,3,4] - @user_options[key].keys).empty?
24
+ end
25
+ end
26
+ end
27
+
28
+ def default_options
29
+ output = Hash.new()
30
+ output[:display_cards_per_row] = DISPLAY_CARDS_PER_ROW
31
+ output[:winning_score] = WINNING_SCORE
32
+ output[:min_to_take_two] = MIN_TO_TAKE_TWO
33
+ output[:starting_gold_tokens] = STARTING_GOLD_TOKENS
34
+ output[:starting_non_gold_tokens] = STARTING_NON_GOLD_TOKENS
35
+ output[:nobles_available] = NOBLES_AVAILABLE
36
+ output[:player_token_limit] = PLAYER_TOKEN_LIMIT
37
+ output
38
+ end
39
+
40
+ #Take the user values if they are valid, else use defaults
41
+ def give_options
42
+ clean_user_options
43
+ @user_options.merge(default_options)
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,33 @@
1
+ module SplendorGame
2
+
3
+ class Player
4
+
5
+ attr_reader :name, :turn_order, :tableau, :nobles
6
+
7
+ def initialize(name, turn_order, token_limit)
8
+ @name = name
9
+ @turn_order = turn_order
10
+ @tableau = Tableau.new(token_limit)
11
+ @nobles = Array.new()
12
+ end
13
+
14
+ def points
15
+ card_points = @tableau.cards.inject(0) { |sum,c| sum + c.points }
16
+ card_points + @nobles.inject(0) { |sum,c| sum + c.points }
17
+ end
18
+
19
+ def can_afford_noble?(noble)
20
+ noble.cost.each do |k, v|
21
+ return false if @tableau.colours_on_cards(k) < v
22
+ end
23
+ true
24
+ end
25
+
26
+ def claim_noble(noble)
27
+ return false if !can_afford_noble?(noble)
28
+ @nobles << noble
29
+ true
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,128 @@
1
+ module SplendorGame
2
+
3
+
4
+ class Tableau
5
+ @@max_reserved_cards = 3
6
+ attr_reader :cards, :tokens, :reserved_cards
7
+
8
+ def initialize(token_limit)
9
+ @cards = Array.new()
10
+ @tokens = Hash.new(0)
11
+ @unlimited = token_limit <= 0 ? true : false
12
+ @token_limit = token_limit
13
+ @reserved_cards = Array.new()
14
+ end
15
+
16
+ def seed_bank(args)
17
+ seed_bank_non_gold(args[:options][:starting_non_gold_tokens][args[:player_count]])
18
+ seed_bank_gold(args[:options][:starting_gold_tokens])
19
+ end
20
+
21
+ ### add tokens, remove tokens, counting tokens
22
+
23
+ def add_token(token_colour)
24
+ return false if !VALID_COLOUR_SYMBOLS.include?(token_colour)
25
+ return false if !@unlimited && token_count >= @token_limit
26
+ if @tokens.include?(token_colour)
27
+ @tokens[token_colour] += 1
28
+ else
29
+ @tokens[token_colour] = 1
30
+ end
31
+ true
32
+ end
33
+
34
+ def remove_token(token_colour)
35
+ return false if !@tokens.key?(token_colour)
36
+ return false if @tokens[token_colour] <= 0
37
+ @tokens[token_colour] -= 1
38
+ true
39
+ end
40
+
41
+ def token_count
42
+ @tokens.inject(0) { |sum,(_k,v)| sum + v }
43
+ end
44
+
45
+ def token_space_remaining
46
+ return nil if @unlimited == true #meaning, undefined
47
+ @token_limit - token_count
48
+ end
49
+
50
+ def distinct_token_colour_count
51
+ @tokens.count { |_k,v| v >0 }
52
+ end
53
+
54
+ #### reserving cards
55
+
56
+ def can_reserve_card?
57
+ return false if @reserved_cards.size >= @@max_reserved_cards
58
+ true
59
+ end
60
+
61
+ def reserve_card(card)
62
+ return false if !can_reserve_card?
63
+ @reserved_cards << card
64
+ end
65
+
66
+ def play_reserved_card(card)
67
+ return false if tokens_required(card) == false
68
+ return false unless @reserved_cards.include?(card)
69
+ @cards << card
70
+ @reserved_cards.delete(card)
71
+ end
72
+
73
+ ### related to purchasing cards
74
+
75
+ def purchase_card(card)
76
+ return false if tokens_required(card) == false
77
+ @cards << card
78
+ end
79
+
80
+ # Returns a Hash of the tokens required to buy the card
81
+ # If the tableau does not have sufficient cards/tokens, it returns false
82
+ def tokens_required(card)
83
+ answer = Hash.new(0)
84
+ card.cost.each do |colour, col_cost|
85
+ theoretical_tokens = col_cost - colours_on_cards(colour)
86
+ if theoretical_tokens <= @tokens[colour]
87
+ answer[colour] = theoretical_tokens if theoretical_tokens > 0
88
+ else
89
+ answer[colour] = @tokens[colour]
90
+ answer[:gold] += theoretical_tokens - @tokens[colour]
91
+ end
92
+ end
93
+ return false if answer[:gold] > @tokens[:gold]
94
+ answer
95
+ end
96
+
97
+ def all_colours_on_cards
98
+ output = Hash.new()
99
+ VALID_COLOUR_SYMBOLS.each { |c| output[c] = colours_on_cards(c) if colours_on_cards(c) > 0 }
100
+ output
101
+ end
102
+
103
+ def colours_on_cards(colour)
104
+ @cards.inject(0) { |sum,card| card.colour == colour ? sum+1 : sum }.to_i
105
+ end
106
+
107
+ ### Other
108
+
109
+ def is_empty?
110
+ @cards.size==0 && @tokens.size==0 ? true : false
111
+ end
112
+
113
+ ### setup
114
+ private
115
+
116
+ def seed_bank_non_gold(token_count)
117
+ VALID_COLOUR_SYMBOLS.each do |colour|
118
+ @tokens[colour] = token_count if colour != :gold
119
+ end
120
+ end
121
+
122
+ def seed_bank_gold(token_count)
123
+ @tokens[:gold] = token_count
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,144 @@
1
+ module SplendorGame
2
+
3
+ class Turn
4
+ attr_reader :player, :action_done
5
+
6
+ def initialize(game, player)
7
+ @game = game
8
+ @player = player
9
+ @action_done = false
10
+ @noble_claimed = false
11
+ end
12
+
13
+ #from the 12 cards on display plus up to 3 reserved ones, return the ones affordable, in an Array
14
+ def affordable_cards
15
+ answer = Array.new()
16
+ (@game.all_displayed_cards + @player.tableau.reserved_cards).each do |card|
17
+ @cost = @player.tableau.tokens_required(card)
18
+ answer << card if !@cost==false
19
+ end
20
+ answer
21
+ end
22
+
23
+ def purchase_card(card) # do the requisite card/token changes, if the card is in affordable_cards
24
+ return false if @action_done
25
+ return false if !affordable_cards.include?(card)
26
+ cost_to_player = @player.tableau.tokens_required(card)
27
+ if @player.tableau.reserved_cards.include?(card)
28
+ @player.tableau.play_reserved_card(card)
29
+ else
30
+ @player.tableau.purchase_card(card)
31
+ end
32
+ cost_to_player.each do |colour, val|
33
+ val.times { @player.tableau.remove_token(colour) }
34
+ val.times { @game.bank.add_token(colour) }
35
+ end
36
+ display_row = @game.display[card.level]
37
+ display_row.delete_at(display_row.index(card) || display_row.length)
38
+ @action_done = true
39
+ end
40
+
41
+ def reserve_card_checks?
42
+ return false if @action_done
43
+ return false if !@player.tableau.can_reserve_card?
44
+ true
45
+ end
46
+
47
+ def give_gold_token
48
+ if @game.bank.tokens[:gold] > 0 && @player.tableau.token_space_remaining > 0
49
+ @game.bank.remove_token(:gold)
50
+ @player.tableau.add_token(:gold)
51
+ end
52
+ end
53
+
54
+ def reserve_displayed_card(card) # put cards in to player's reserved set. Remove card from display
55
+ return false if !reserve_card_checks?
56
+ return false if !@game.display.flatten(2).include?(card)
57
+ @player.tableau.reserve_card(card)
58
+ display_row = @game.display[card.level]
59
+ display_row.delete_at(display_row.index(card) || display_row.length)
60
+ give_gold_token
61
+ @action_done = true
62
+ end
63
+
64
+ def reserve_random_card(deck_number)
65
+ return false if !reserve_card_checks?
66
+ return false if @game.deck[deck_number].nil?
67
+ return false if @game.deck[deck_number].count==0
68
+ card = @game.deck[deck_number].pop
69
+ @player.tableau.reserve_card(card)
70
+ give_gold_token
71
+ @action_done = true
72
+ end
73
+
74
+ def validate_token_pickup?(colour)
75
+ return false if @action_done
76
+ return false if Array(colour).include?(:gold)
77
+ true
78
+ end
79
+
80
+ def take_two_tokens_same_colour(colour)
81
+ colour = colour.to_sym
82
+ return false if !validate_token_pickup?(colour)
83
+ return false if @game.bank.tokens[colour] < @game.options[:min_to_take_two]
84
+ return false if @player.tableau.token_space_remaining < 2
85
+ 2.times { @player.tableau.add_token(colour) }
86
+ 2.times { @game.bank.remove_token(colour) }
87
+ @action_done = true
88
+ end
89
+
90
+ def take_different_tokens(colours)
91
+ colours.map!{|c| c.to_sym}
92
+ return false if !validate_token_pickup?(colours)
93
+ return false if colours.count != 3
94
+ return false if colours.uniq.length != colours.length
95
+ return false if colours.select { |c| @game.bank.tokens[c]==0}.length > 0
96
+ return false if @player.tableau.token_space_remaining < 3
97
+ colours.each do |c|
98
+ @player.tableau.add_token(c)
99
+ @game.bank.remove_token(c)
100
+ end
101
+ @action_done = true
102
+ end
103
+
104
+ # in order to claim tokens you may need to return tokens, eg pick up a gold when
105
+ # you have 10, or you have 8 and want to take 3 different.
106
+
107
+ def action_return_token(colour)
108
+ return false if !@player.tableau.tokens.include?(colour)
109
+ return false if @player.tableau.tokens[colour] <= 0
110
+ @player.tableau.remove_token(colour)
111
+ @game.bank.add_token(colour)
112
+ end
113
+
114
+ def claim_noble(noble) # Move noble from bank to player, assuming it's affordable
115
+ return false if @noble_claimed # you can only claim 1 per turn
116
+ return false if !noble_affordable?(noble)
117
+ @game.nobles.delete_at(@game.nobles.index(noble) || display_row.length)
118
+ @player.nobles << noble
119
+ @noble_claimed = true
120
+ end
121
+
122
+ def claimable_nobles
123
+ @game.nobles.find_all { |noble| noble_affordable?(noble) }
124
+ end
125
+
126
+ def noble_affordable?(noble)
127
+ noble.cost.each do |colour, cost|
128
+ return false if @player.tableau.colours_on_cards(colour) < cost
129
+ end
130
+ true
131
+ end
132
+
133
+ def end_turn # make sure there are 4 cards showing from each deck, assuming there are any left
134
+ #assumption : max 1 card is missing from each row
135
+ nonfull_rows = @game.display.select { |_level, subdeck| subdeck.count < @game.options[:display_cards_per_row] }
136
+ nonfull_rows.each do |row_num, display_row|
137
+ relevant_deck = @game.deck[row_num]
138
+ display_row << relevant_deck.pop if relevant_deck.count > 0
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ end
@@ -0,0 +1,3 @@
1
+ module SplendorGame
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'splendor_game/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "splendor_game"
8
+ spec.version = SplendorGame::VERSION
9
+ spec.authors = ["reedstonefood"]
10
+ spec.email = ["reedstonefood@users.noreply.github.com"]
11
+
12
+ spec.summary = %q{An implementation of the board game, Splendor.}
13
+ spec.homepage = "https://github.com/reedstonefood/splendor_game"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.13"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+
27
+ spec.add_dependency "highline", "~> 1.7"
28
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: splendor_game
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - reedstonefood
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-08-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: highline
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ description:
70
+ email:
71
+ - reedstonefood@users.noreply.github.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".codeclimate.yml"
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/console
85
+ - bin/setup
86
+ - lib/splendor_game.rb
87
+ - lib/splendor_game/card.rb
88
+ - lib/splendor_game/cli.rb
89
+ - lib/splendor_game/coloured_object.rb
90
+ - lib/splendor_game/game.rb
91
+ - lib/splendor_game/load_cards.rb
92
+ - lib/splendor_game/noble.rb
93
+ - lib/splendor_game/options.rb
94
+ - lib/splendor_game/player.rb
95
+ - lib/splendor_game/tableau.rb
96
+ - lib/splendor_game/turn.rb
97
+ - lib/splendor_game/version.rb
98
+ - splendor_game.gemspec
99
+ homepage: https://github.com/reedstonefood/splendor_game
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.6.6
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: An implementation of the board game, Splendor.
123
+ test_files: []