gatherer_set_parser 0.0.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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gatherer_set_parser.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 David Ratajczak
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # GathererSetParser
2
+
3
+ This is a tool for parsing the official Magic the Gathering card database for
4
+ a complete collection of cards belonging to a specific set. The cards of the
5
+ given set will be returned in an array of hashes.
6
+
7
+ This gem was extracted from a personal project, and hasn't been throughly
8
+ tested for every set in production. You'll likely want to refactor a lot of
9
+ the code before using it on a production server.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'gatherer_set_parser'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install gatherer_set_parser
24
+
25
+ ## Usage
26
+
27
+ This set requires you to pass in a case sensitive set name from the Gatherer
28
+ website. Here's an example:
29
+
30
+ ```
31
+ # Create the array of cards from the given set name
32
+ cards = GathererSetParser::Fetch.new('Dark Ascension').cards
33
+ ```
34
+
35
+ This will return an array of 171 cards. Here's an example of the output of
36
+ cards.first:
37
+
38
+ ```
39
+ { :title=> "Afflicted Deserter",
40
+ :card_type => "Creature",
41
+ :sub_type => "",
42
+ :power => 3,
43
+ :toughness => 2,
44
+ :cmc => 4,
45
+ :mana_cost => ["3", "R"],
46
+ :loyalty => nil,
47
+ :image_url => "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=262675&type=card",
48
+ :text => "\r\n At the beginning of each upkeep, if no spells were cast last turn, transform Afflicted Deserter.",
49
+ :gatherer_url => "http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=262675",
50
+ :rarity => "Uncommon" }
51
+ ```
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create new Pull Request
60
+
61
+ ## Contact
62
+
63
+ [david@mockra.com](mailto:david@mockra.com)
64
+
65
+ [@Mockra_](http://twitter.com/#!/mockra_)
66
+
67
+ [mockra.com](http://mockra.com)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/gatherer_set_parser/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["David Ratajczak"]
6
+ gem.email = ["david@mockra.com"]
7
+ gem.description = %q{Parses the MTG Gatherer website for a collection of
8
+ cards from the provided set.}
9
+ gem.summary = %q{This gem will return an array of card hashes that you
10
+ can use to build a database of Magic the Gathering cards.}
11
+ gem.homepage = "https://github.com/mockra/gatherer_set_parser"
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.name = "gatherer_set_parser"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = GathererSetParser::VERSION
19
+
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec'
22
+ gem.add_development_dependency 'fakeweb'
23
+ gem.add_development_dependency 'vcr'
24
+ gem.add_dependency 'nokogiri'
25
+ end
@@ -0,0 +1,114 @@
1
+ module GathererSetParser
2
+ class CardParser
3
+
4
+ attr_accessor :body
5
+
6
+ def initialize card
7
+ @body = card
8
+ end
9
+
10
+ def attributes
11
+ { title: title,
12
+ card_type: type,
13
+ sub_type: sub_type,
14
+ power: power,
15
+ toughness: toughness,
16
+ cmc: cmc,
17
+ mana_cost: mana_cost,
18
+ loyalty: loyalty,
19
+ image_url: image_url,
20
+ text: text,
21
+ gatherer_url: gatherer_url,
22
+ rarity: rarity }
23
+ end
24
+
25
+ def title
26
+ body.css('.cardTitle > a').text
27
+ end
28
+
29
+ def type_box
30
+ body.css('span.typeLine').text.
31
+ gsub(/\n/, '').gsub(/[ ]{2,}/, ' ').strip.lstrip
32
+ end
33
+
34
+ def type
35
+ type_box.match(/[a-z0-9 ]*/i).to_s.strip
36
+ end
37
+
38
+ def sub_type
39
+ type_box.gsub(/\(.*\)/, '').
40
+ match(/ ([a-z0-9 ]*)\z/i)[1].to_s.lstrip.strip
41
+ rescue
42
+ nil
43
+ end
44
+
45
+ def power
46
+ type_box.match(/\(([0-9]*)\//)[1].to_i rescue nil
47
+ end
48
+
49
+ def toughness
50
+ type_box.match(/\/([0-9]*)\)/)[1].to_i rescue nil
51
+ end
52
+
53
+ def cmc
54
+ body.css('.convertedManaCost').text.to_i
55
+ end
56
+
57
+ def loyalty
58
+ type_box.match(/\(([0-9]*)\)/)[1].to_i rescue nil
59
+ end
60
+
61
+ def rarity
62
+ body.css('.rightCol > a').first.children.attribute('alt').text.
63
+ match(/\((.*)\)/)[1].to_s
64
+ end
65
+
66
+ def text
67
+ body.xpath('//div[@class="rulesText"]//img[@alt]').each{ |i| i.swap( i['alt'].parse_mana_cost ) }
68
+ body.css('.rulesText').text.gsub(/[ ]{2,}/, ' ')
69
+ end
70
+
71
+ def mana_cost
72
+ array = []
73
+ body.css('.manaCost >img').each do |img|
74
+ array << img.attribute('alt').text.parse_mana_cost
75
+ end
76
+ array
77
+ end
78
+
79
+ def image_url
80
+ 'http://gatherer.wizards.com/' +
81
+ body.css('.leftCol > a > img').attribute('src').
82
+ text.gsub(/\.\.\//, '')
83
+ end
84
+
85
+ def gatherer_url
86
+ 'http://gatherer.wizards.com/Pages/' +
87
+ body.css('.cardTitle > a').attribute('href').
88
+ text.gsub(/\.\.\//, '')
89
+ end
90
+
91
+ end
92
+ end
93
+
94
+ class String
95
+ def parse_mana_cost
96
+ extract_phyrexia.extract_colors.extract_unique_costs
97
+ end
98
+
99
+ def extract_phyrexia
100
+ gsub(/Phyrexian Blue/, 'U/P').gsub(/Phyrexian Black/, 'B/P').
101
+ gsub(/Phyrexian White/, 'W/P').gsub(/Phyrexian Red/, 'R/P').
102
+ gsub(/Phyrexian Green/, 'G/P')
103
+ end
104
+
105
+ def extract_colors
106
+ gsub(/Blue/, 'U').gsub(/Black/, 'B').gsub(/Red/, 'R').gsub(/Green/, 'G').
107
+ gsub(/White/, 'W')
108
+ end
109
+
110
+ def extract_unique_costs
111
+ gsub(/Variable Colorless/, 'X').gsub(/Two/, '2').gsub(/or/, '/').
112
+ gsub(/Snow/, 'S').gsub(/Tap/, 'T').gsub(/Untap/, 'Q')
113
+ end
114
+ end
@@ -0,0 +1,46 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+
4
+ module GathererSetParser
5
+ class Fetch
6
+
7
+ include GathererSetParser::QueryString
8
+
9
+ attr_reader :set_name
10
+
11
+ def response_page page
12
+ Nokogiri::HTML open(gatherer_url page), nil, 'UTF-8'
13
+ end
14
+
15
+ def complete_set
16
+ @complete_set ||= gather_set
17
+ end
18
+
19
+ def gather_set
20
+ set, page = [], 0
21
+ current_page = response_page page
22
+ total_pages = current_page.css('.paging > a').count
23
+ begin
24
+ set << current_page
25
+ page += 1
26
+ current_page = response_page page
27
+ end while page < total_pages - 1
28
+ set
29
+ end
30
+
31
+ def cards
32
+ @cards ||= collect_cards
33
+ end
34
+
35
+ def collect_cards
36
+ collection = []
37
+ complete_set.each do |page|
38
+ page.css('.cardItemTable > tr > td > table').each do |card|
39
+ collection << CardParser.new(card).attributes
40
+ end
41
+ end
42
+ collection
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ module GathererSetParser
2
+ module QueryString
3
+
4
+ def initialize set
5
+ @set_name = set
6
+ end
7
+
8
+ def escaped_set
9
+ @query_string ||= URI.escape set_name
10
+ end
11
+
12
+ def query_string
13
+ "output=standard&set=[%22#{escaped_set}%22]"
14
+ end
15
+
16
+ def url
17
+ "http://gatherer.wizards.com/Pages/Search/Default.aspx?"
18
+ end
19
+
20
+ def gatherer_url page
21
+ url + query_string + "&page=#{page}"
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module GathererSetParser
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "gatherer_set_parser/version"
2
+ require 'gatherer_set_parser/query_string'
3
+ require 'gatherer_set_parser/card_parser'
4
+ require 'gatherer_set_parser/fetch'
5
+
6
+ module GathererSetParser
7
+ end
@@ -0,0 +1,181 @@
1
+ require 'gatherer_set_parser_spec'
2
+ require 'fakeweb'
3
+
4
+ describe GathererSetParser::CardParser do
5
+
6
+ let(:parsed_card) { GathererSetParser::CardParser.new card }
7
+
8
+ describe '#parse_card' do
9
+ let(:card) { Nokogiri::HTML open('spec/support/flipped_creature.html') }
10
+
11
+ it 'should return a hash' do
12
+ parsed_card.attributes.should be_a Hash
13
+ end
14
+
15
+ it 'should have a title' do
16
+ parsed_card.attributes[:title].should_not be_nil
17
+ end
18
+ end
19
+
20
+ describe '#card_attributes' do
21
+ context 'back of flip card' do
22
+ let(:card) { Nokogiri::HTML open('spec/support/flipped_creature.html') }
23
+
24
+ it 'should return the correct title' do
25
+ parsed_card.title.should == "Tovolar's Magehunter"
26
+ end
27
+
28
+ it 'should return a card type' do
29
+ parsed_card.type.should == 'Creature'
30
+ end
31
+
32
+ it 'should match the species type' do
33
+ parsed_card.sub_type.should == 'Werewolf'
34
+ end
35
+
36
+ it 'should have the correct power' do
37
+ parsed_card.power.should == 5
38
+ end
39
+
40
+ it 'should have the correct toughness' do
41
+ parsed_card.toughness.should == 5
42
+ end
43
+
44
+ it 'should have no loyalty' do
45
+ parsed_card.loyalty.should be_nil
46
+ end
47
+
48
+ it 'should have a cmc' do
49
+ parsed_card.cmc.should == 0
50
+ end
51
+
52
+ it 'should have the correct rarity' do
53
+ parsed_card.rarity.should == 'Rare'
54
+ end
55
+
56
+ it 'should have text' do
57
+ parsed_card.text.should =~ /transform/i
58
+ end
59
+
60
+ it 'should have the correct mana cost' do
61
+ parsed_card.mana_cost.should == []
62
+ end
63
+
64
+ it 'should have the correct image url' do
65
+ parsed_card.image_url.should =~
66
+ /\/Handlers\/Image\.ashx\?multiverseid\=253429\&type\=card/
67
+ end
68
+ end
69
+
70
+ context 'an instant/sorcery' do
71
+ let(:card) { Nokogiri::HTML open('spec/support/instant.html') }
72
+
73
+ it 'should have no power' do
74
+ parsed_card.power.should be_nil
75
+ end
76
+
77
+ it 'should have no loyalty' do
78
+ parsed_card.loyalty.should be_nil
79
+ end
80
+
81
+ it 'should have the correct type' do
82
+ parsed_card.type.should == 'Instant'
83
+ end
84
+
85
+ it 'should have the correct rarity' do
86
+ parsed_card.rarity.should == 'Uncommon'
87
+ end
88
+
89
+ it 'should have the correct mana cost' do
90
+ parsed_card.mana_cost.should == %w[1 U]
91
+ end
92
+
93
+ it 'should have no sub type' do
94
+ parsed_card.sub_type.should be_nil
95
+ end
96
+ end
97
+
98
+ context 'land' do
99
+ let(:card) { Nokogiri::HTML open('spec/support/land.html') }
100
+
101
+ it 'should have the correct mana cost' do
102
+ parsed_card.mana_cost.should == []
103
+ end
104
+
105
+ it 'should have no power' do
106
+ parsed_card.power.should be_nil
107
+ end
108
+ end
109
+
110
+ context 'planeswalker' do
111
+ let(:card) { Nokogiri::HTML open('spec/support/planeswalker.html') }
112
+
113
+ it 'should have a title' do
114
+ parsed_card.title.should =~ /Tamiyo/i
115
+ end
116
+
117
+ it 'should have no power' do
118
+ parsed_card.power.should be_nil
119
+ end
120
+
121
+ it 'should have no toughness' do
122
+ parsed_card.power.should be_nil
123
+ end
124
+
125
+ it 'should have loyalty' do
126
+ parsed_card.loyalty.should == 4
127
+ end
128
+
129
+ it 'should have the correct type' do
130
+ parsed_card.type.should == 'Planeswalker'
131
+ end
132
+
133
+ it 'should have the correct rarity' do
134
+ parsed_card.rarity.should == 'Mythic Rare'
135
+ end
136
+
137
+ it 'should have the correct mana cost' do
138
+ parsed_card.mana_cost.should == %w[3 U U]
139
+ end
140
+
141
+ it 'should have correct gatherer url' do
142
+ parsed_card.gatherer_url.should =~
143
+ /Card\/Details\.aspx\?multiverseid\=240070/
144
+ end
145
+ end
146
+
147
+ context 'phyrexian' do
148
+ let(:card) { Nokogiri::HTML open('spec/support/phyrexia.html') }
149
+
150
+ it 'should have the correct mana cost' do
151
+ parsed_card.mana_cost.should == %w[X G/P]
152
+ end
153
+ end
154
+
155
+ context 'enchant' do
156
+ let(:card) { Nokogiri::HTML open('spec/support/enchant.html') }
157
+
158
+ it 'should have the correct mana cost' do
159
+ parsed_card.mana_cost.should == %w[4 B B]
160
+ end
161
+ end
162
+
163
+ context 'gold' do
164
+ end
165
+
166
+ context 'dual_land' do
167
+ let(:card) { Nokogiri::HTML open('spec/support/dual_land.html') }
168
+
169
+ it 'should have the correct text' do
170
+ parsed_card.text.should =~ /U/i
171
+ end
172
+ end
173
+
174
+ context 'legendary' do
175
+ end
176
+
177
+ context 'artifact' do
178
+ end
179
+ end
180
+
181
+ end
@@ -0,0 +1,30 @@
1
+ require 'gatherer_set_parser_spec'
2
+
3
+ describe GathererSetParser::Fetch do
4
+
5
+ let(:gatherer) { GathererSetParser::Fetch.new 'Dark Ascension' }
6
+
7
+ describe '.new' do
8
+ it 'should assign the set_name' do
9
+ gatherer.set_name.should == 'Dark Ascension'
10
+ end
11
+ end
12
+
13
+ describe '#response_body' do
14
+ use_vcr_cassette 'dark_ascension'
15
+
16
+ it 'should contain every page' do
17
+ gatherer.complete_set.count.should == 7
18
+ end
19
+
20
+ it 'should contain a magic card' do
21
+ gatherer.complete_set.last.text.should =~ /wakedancer/i
22
+ end
23
+
24
+ it 'should have the complete set of cards' do
25
+ gatherer.cards.count.should == 171
26
+ end
27
+ end
28
+
29
+ end
30
+
@@ -0,0 +1,21 @@
1
+ require 'gatherer_set_parser_spec'
2
+
3
+ describe GathererSetParser::QueryString do
4
+
5
+ let(:gatherer) { GathererSetParser::Fetch.new 'Dark Ascension' }
6
+
7
+ describe '#escaped_set' do
8
+ it 'should escape set_name to query string' do
9
+ gatherer.escaped_set.should == 'Dark%20Ascension'
10
+ end
11
+ end
12
+
13
+ describe '#query_string' do
14
+ it 'should contain the entire query' do
15
+ gatherer.query_string.should ==
16
+ 'output=standard&set=[%22Dark%20Ascension%22]'
17
+ end
18
+ end
19
+
20
+ end
21
+
@@ -0,0 +1,2 @@
1
+ require 'gatherer_set_parser'
2
+ require 'vcr_setup'
@@ -0,0 +1,35 @@
1
+ <table>
2
+ <tr class="cardItem evenItem">
3
+ <td class="leftCol">
4
+ <div class="clear"></div>
5
+ <a href="../Card/Details.aspx?multiverseid=230070" id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl00_cardImageLink" onclick="return CardLinkAction(event, this, 'SameWindow');">
6
+ <img src="../../Handlers/Image.ashx?multiverseid=230070&amp;type=card" id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl00_cardImage" width="95" height="132" alt="Conversion Chamber" border="0" />
7
+ </a>
8
+ <div class="clear"></div>
9
+ </td>
10
+ <td class="middleCol">
11
+ <div class="clear"></div>
12
+ <div class="cardInfo">
13
+ <span class="cardTitle">
14
+ <a id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl00_cardTitle" onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=230070">Conversion Chamber</a></span> <span class="manaCost">
15
+ <img src="/Handlers/Image.ashx?size=small&amp;name=3&amp;type=symbol" alt="3" align="absbottom" /></span> (<span class="convertedManaCost">3</span>)
16
+ <br />
17
+ <span class="typeLine">
18
+ Artifact
19
+ </span>
20
+ <br />
21
+ <div class="rulesText">
22
+ <p><img src="/Handlers/Image.ashx?size=small&amp;name=2&amp;type=symbol" alt="2" align="absbottom" />, <img src="/Handlers/Image.ashx?size=small&amp;name=tap&amp;type=symbol" alt="Tap" align="absbottom" />: Exile target artifact card from a graveyard. Put a charge counter on Conversion Chamber.</p><p><img src="/Handlers/Image.ashx?size=small&amp;name=2&amp;type=symbol" alt="2" align="absbottom" />, <img src="/Handlers/Image.ashx?size=small&amp;name=tap&amp;type=symbol" alt="Tap" align="absbottom" />, Remove a charge counter from Conversion Chamber: Put a 3/3 colorless Golem artifact creature token onto the battlefield.</p></div>
23
+ </div>
24
+ </td>
25
+ <td class="rightCol setVersions">
26
+ <div class="clear"></div>
27
+ <div>
28
+ <div id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl00_cardSetCurrent" class="rightCol">
29
+ <a onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=230070"><img title="New Phyrexia (Uncommon)" src="../../Handlers/Image.ashx?type=symbol&amp;set=NPH&amp;size=small&amp;rarity=U" alt="New Phyrexia (Uncommon)" style="border-width:0px;" /></a>
30
+ </div>
31
+ </div>
32
+
33
+ </td>
34
+ </tr>
35
+ </table>
@@ -0,0 +1,40 @@
1
+ <table>
2
+ <tr class="cardItem oddItem">
3
+ <td class="leftCol">
4
+ <div class="clear"></div>
5
+ <a href="../Card/Details.aspx?multiverseid=253682" id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardImageLink" onclick="return CardLinkAction(event, this, 'SameWindow');">
6
+ <img src="../../Handlers/Image.ashx?multiverseid=253682&amp;type=card" id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardImage" width="95" height="132" alt="Steam Vents" border="0" />
7
+ </a>
8
+ <div class="clear"></div>
9
+ </td>
10
+ <td class="middleCol">
11
+ <div class="clear"></div>
12
+ <div class="cardInfo">
13
+ <span class="cardTitle">
14
+ <a id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardTitle" onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=253682">Steam Vents</a></span> <span class="manaCost">
15
+ </span> (<span class="convertedManaCost">0</span>)
16
+ <br />
17
+ <span class="typeLine">
18
+ Land — Island Mountain
19
+ </span>
20
+ <div class="rulesText">
21
+ <p><i>(<img src="/Handlers/Image.ashx?size=small&amp;name=tap&amp;type=symbol" alt="Tap" align="absbottom" />: Add <img src="/Handlers/Image.ashx?size=small&amp;name=U&amp;type=symbol" alt="Blue" align="absbottom" /> or <img src="/Handlers/Image.ashx?size=small&amp;name=R&amp;type=symbol" alt="Red" align="absbottom" /> to your mana pool.)</i></p><p>As Steam Vents enters the battlefield, you may pay 2 life. If you don't, Steam Vents enters the battlefield tapped.</p></div>
22
+ </div>
23
+ </td>
24
+ <td class="rightCol setVersions">
25
+ <div class="clear"></div>
26
+ <div>
27
+ <div id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardSetCurrent" class="rightCol">
28
+ <a onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=253682"><img title="Return to Ravnica (Rare)" src="../../Handlers/Image.ashx?type=symbol&amp;set=RTR&amp;size=small&amp;rarity=R" alt="Return to Ravnica (Rare)" style="border-width:0px;" /></a>
29
+ </div>
30
+ </div>
31
+ <div id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_otherSetSection" class="otherSetSection">
32
+ <div>
33
+ <i>Other Versions</i></div>
34
+ <div id="ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl00_listRepeater_ctl11_cardSets" class="rightCol">
35
+ <a onclick="return CardLinkAction(event, this, 'SameWindow');" href="../Card/Details.aspx?multiverseid=96923"><img title="Guildpact (Rare)" src="../../Handlers/Image.ashx?type=symbol&amp;set=GPT&amp;size=small&amp;rarity=R" alt="Guildpact (Rare)" style="border-width:0px;" /></a>
36
+ </div>
37
+ </div>
38
+ </td>
39
+ </tr>
40
+ </table>