ascension 0.1.0

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.
@@ -0,0 +1,283 @@
1
+ module Parse
2
+ def self.reg_word(word,&b)
3
+ Words.instance.reg_word(word,&b)
4
+ end
5
+ def self.reg_ability(word,ability=nil,&b)
6
+ ability ||= b
7
+ Words.instance.reg_word(word) { |side| ability.call(side) }
8
+ end
9
+ def self.cards
10
+ @cards ||= InputFile.new.cards
11
+ end
12
+ def self.get(name)
13
+ cards.find { |x| x.name == name }.tap { |x| raise "no card #{name}" unless x }
14
+ end
15
+
16
+ class Words
17
+ class << self
18
+ fattr(:instance) { new }
19
+ end
20
+ fattr(:words) { {} }
21
+ fattr(:abilities) { {} }
22
+ def reg_word(word,&b)
23
+ words[word.to_s] = b
24
+ words["first_#{word}"] = lambda do |event|
25
+ b.call(event) && event.first
26
+ end
27
+ end
28
+ def reg_ability(word,ability)
29
+ abilities[word.to_s] = ability
30
+ end
31
+ end
32
+
33
+ class Word
34
+ include FromHash
35
+ attr_accessor :raw
36
+ def self.parsed(ops)
37
+ new(ops)
38
+ end
39
+ fattr(:word_blk) do
40
+ Words.instance.words[raw.to_s] || (raise "no block for #{raw}")
41
+ end
42
+ def occured?(side)
43
+ if word_blk.arity == 1
44
+ side.events.cond?(&word_blk)
45
+ else
46
+ word_blk[side,nil]
47
+ end
48
+ end
49
+ end
50
+
51
+ module Phrase
52
+ def self.phrase_class(raw)
53
+ a = raw.split(" ")
54
+ h = {"on" => On, "if" => If, "for" => For}
55
+ if a.size == 3
56
+ h[a[1]]
57
+ else
58
+ Basic
59
+ end
60
+ end
61
+ def self.parsed(raw)
62
+ cls = phrase_class(raw)
63
+ cls.new(:raw => raw)
64
+ end
65
+
66
+ class Base
67
+ include FromHash
68
+ attr_accessor :raw, :category
69
+ fattr(:before_clause_raw) { raw.split(" ").first }
70
+ fattr(:before_clause) do
71
+ before_clause_raw[0..0] == 'o' ? before_clause_raw[1..-1] : before_clause_raw
72
+ end
73
+ fattr(:optional) do
74
+ before_clause_raw[0..0] == 'o'
75
+ end
76
+ fattr(:after_clause) { raw.split(" ").last }
77
+ fattr(:after_word) do
78
+ Word.parsed(:raw => after_clause)
79
+ end
80
+ def trigger; nil; end
81
+ def ability; nil; end
82
+ def mod_card(card)
83
+ card.triggers << trigger.tap { |x| x.optional = optional if x.respond_to?('optional=') } if trigger
84
+ card.abilities << ability.tap { |x| x.optional = optional if x.respond_to?('optional=') } if ability
85
+ end
86
+
87
+ def add_honor(side)
88
+ side.honor += before_clause.to_i
89
+ end
90
+ def add_power(side)
91
+ side.pool.power += before_clause.to_i
92
+ end
93
+ def draw_cards(side)
94
+ before_clause.to_i.times do
95
+ side.draw_one!
96
+ end
97
+ end
98
+ end
99
+
100
+ class Basic < Base
101
+ def mod_card(card)
102
+ if category == :runes
103
+ card.runes += before_clause.to_i if before_clause.to_i > 0
104
+ elsif category == :power || category == :add_power
105
+ card.power += before_clause.to_i
106
+ elsif category == :draw_cards
107
+ card.abilities << lambda do |side|
108
+ draw_cards(side)
109
+ end
110
+ elsif category.kind_of?(Class)
111
+ card.abilities << category.new(:optional => optional, :parent_card => card)
112
+ else
113
+ raise "unknown category #{category}"
114
+ end
115
+ end
116
+ end
117
+
118
+ class On < Base
119
+ fattr(:trigger) do
120
+ lambda do |event, side|
121
+ if after_word.word_blk[event]
122
+ send(category, side)
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ class If < Base
129
+ fattr(:ability) do
130
+ lambda do |side|
131
+ if after_word.occured?(side)
132
+ send(category, side)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ class For < Base
139
+ fattr(:ability) do
140
+ lambda do |side|
141
+ meth = "#{after_clause}_runes"
142
+ val = side.played.pool.send(meth) + before_clause.to_i
143
+ side.played.pool.send("#{meth}=",val)
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ class Card
150
+ include FromHash
151
+ def self.input_field(*args)
152
+ attr_accessor *args
153
+ end
154
+ input_field :rune_cost, :honor_given, :power, :runes, :draw
155
+ input_field :banish_center, :banish_hand_discard
156
+ input_field :special_abilities, :realm, :name, :honor, :power_cost
157
+ fattr(:card_class) do
158
+ ::Card::Hero
159
+ end
160
+ def phrase(raw, cat)
161
+ return nil unless raw
162
+ Phrase.parsed(raw).tap { |x| x.category = cat }
163
+ end
164
+ def mod_for_phrases(raw, cat, card)
165
+ return unless raw
166
+ #puts [raw,cat,card_class,name].inspect
167
+ raw.split(",").each do |r|
168
+ phrase(r,cat).mod_card(card)
169
+ end
170
+ end
171
+ fattr(:card) do
172
+ res = card_class.new(:name => name, :realm => realm)
173
+
174
+ mod_for_phrases(runes, :runes, res)
175
+ mod_for_phrases(honor_given,:add_honor,res)
176
+ mod_for_phrases(power, :add_power, res)
177
+ mod_for_phrases(draw, :draw_cards, res)
178
+
179
+ mod_for_phrases(banish_center, Ability::BanishCenter, res)
180
+ mod_for_phrases(banish_hand_discard, Ability::BanishHandDiscard, res)
181
+
182
+ if special_abilities
183
+ word = Word.parsed(:raw => special_abilities)
184
+ res.abilities << word.word_blk
185
+ end
186
+
187
+ res.power_cost = power_cost.to_i if res.monster?
188
+ res.rune_cost = rune_cost.to_i unless res.monster?
189
+
190
+ res
191
+ end
192
+ end
193
+
194
+ class Line
195
+ include FromHash
196
+ attr_accessor :raw
197
+ attr_accessor :realm_short
198
+ fattr(:card_class) do
199
+ h = {'H' => ::Card::Hero, 'C' => ::Card::Construct, 'M' => ::Card::Monster}
200
+ h[raw['card_type']] || (raise 'no class')
201
+ end
202
+ fattr(:realm) do
203
+ h = {'L' => :lifebound, 'M' => :mechana, 'V' => :void, 'E' => :enlightened, 'S' => :monster}
204
+ h[raw['realm_short']] || (raise 'no realm')
205
+ end
206
+ fattr(:parse_card) do
207
+ card = Card.new
208
+ %w(card_class realm).each do |f|
209
+ card.send("#{f}=",send(f))
210
+ end
211
+ %w(name rune_cost honor runes power power_cost draw banish_center banish_hand_discard special_abilities).each do |f|
212
+ card.send("#{f}=",raw[f])
213
+ end
214
+ card
215
+ end
216
+ fattr(:cards) do
217
+ raw['count'].to_i.of { parse_card.card! }
218
+ end
219
+ end
220
+
221
+ class InputFile
222
+ fattr(:raw_lines) do
223
+ require 'csv'
224
+ res = []
225
+ f = File.expand_path(File.dirname(__FILE__)) + "/cards.csv"
226
+ CSV.foreach(f,:headers => true, :row_sep => "\n", :quote_char => '"') do |row|
227
+ h = {}
228
+ row.each do |k,v|
229
+ #puts [k,v].inspect
230
+ k = k.downcase.gsub(' ','_')
231
+ h[k] = v
232
+ end
233
+ res << h if h['name']
234
+ end
235
+ res
236
+ end
237
+ fattr(:lines) do
238
+ raw_lines.map { |x| Line.new(:raw => x) }
239
+ end
240
+ fattr(:cards) do
241
+ lines.map { |x| x.cards }.flatten
242
+ end
243
+ end
244
+
245
+
246
+ end
247
+
248
+ Parse.reg_word :lifebound_hero_played do |event|
249
+ event.kind_of?(Event::CardPlayed) && event.card.realm.to_s == 'lifebound' && event.card.kind_of?(Card::Hero)
250
+ end
251
+
252
+ Parse.reg_word :mechana_construct_played do |event|
253
+ event.kind_of?(Event::CardPlayed) && event.card.realm.to_s == 'mechana' && event.card.kind_of?(Card::Construct)
254
+ end
255
+
256
+ Parse.reg_word :center_monster_killed do |event|
257
+ event.kind_of?(Event::MonsterKilled) && event.center
258
+ end
259
+
260
+ Parse.reg_word :two_or_more_constructs do |side,junk|
261
+ side.constructs.size >= 2
262
+ end
263
+
264
+ (2..6).each do |i|
265
+ Parse.reg_ability "kill_monster_#{i}", Ability::KillMonster.new(:max_power => i)
266
+ end
267
+
268
+ (1..10).each do |i|
269
+ Parse.reg_ability "acquire_hero_#{i}", Ability::AcquireHero.new(:max_rune_cost => i)
270
+ end
271
+
272
+ Parse.reg_ability :discard_construct, Ability::DiscardConstruct
273
+ Parse.reg_ability :discard_all_but_one_construct, Ability::KeepOneConstruct
274
+
275
+ Parse.reg_ability :copy_hero, Ability::CopyHero.new
276
+
277
+ %w(power_or_rune_1 take_opponents_card acquire_center).each do |f|
278
+ Parse.reg_ability f do |*args|
279
+ end
280
+ end
281
+
282
+
283
+
@@ -0,0 +1,49 @@
1
+ class Pool
2
+ include FromHash
3
+ setup_mongo_persist :runes, :mechana_runes, :construct_runes, :power
4
+ fattr(:runes) { 0 }
5
+ fattr(:mechana_runes) { 0 }
6
+ fattr(:construct_runes) { 0 }
7
+ fattr(:power) { 0 }
8
+ def use_rune_type(type, max, modify=true)
9
+ raise "bad max" unless max
10
+ pool = send(type)
11
+ if max >= pool
12
+ send("#{type}=",0) if modify
13
+ max - pool
14
+ else
15
+ send("#{type}=",pool - max) if modify
16
+ 0
17
+ end
18
+ end
19
+ def can_purchase?(card)
20
+ remaining = card.rune_cost
21
+ raise "bad rune cost #{card.name}" unless remaining
22
+ if card.mechana? && card.construct?
23
+ remaining = use_rune_type(:mechana_runes,remaining,false)
24
+ end
25
+ if card.construct?
26
+ remaining = use_rune_type(:construct_runes,remaining,false)
27
+ end
28
+ if remaining > 0
29
+ remaining = use_rune_type(:runes,remaining,false)
30
+ end
31
+ remaining == 0
32
+ end
33
+ def deplete_runes(card)
34
+ remaining = card.rune_cost
35
+ if card.mechana? && card.construct?
36
+ remaining = use_rune_type(:mechana_runes,remaining)
37
+ end
38
+ if card.construct?
39
+ remaining = use_rune_type(:construct_runes,remaining)
40
+ end
41
+ if remaining > 0
42
+ remaining = use_rune_type(:runes,remaining)
43
+ end
44
+ raise "not enough runes" if remaining > 0
45
+ end
46
+ def to_s
47
+ "#{runes} (#{mechana_runes}) / #{power}"
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ load File.dirname(__FILE__) + '/../ascension.rb'
3
+
4
+ Ability::CardChoice.chooser = RChoice::CommandLineChooser.new
5
+
6
+ game = Game.new
7
+ side = Side.new(:game => game)
8
+ game.sides << side
9
+
10
+ game.deck = CenterDeck.starting
11
+ game.center.fill!
12
+ side.deck << game.deck.get_one('Temple Librarian')
13
+ #side.deck[-1] = Card::Hero.arha
14
+ side.draw_hand!
15
+ side.hand << game.deck.get_one('Void Thirster')
16
+ side.deck << game.deck.get_one('Void Initiate')
17
+
18
+ while true
19
+
20
+ side.hand.play_all!
21
+
22
+ side.print_status!
23
+
24
+ Ability::DoCenterAction.new.call_until_nil(side) { side.print_status! }
25
+
26
+ side.end_turn!
27
+ end
@@ -0,0 +1,11 @@
1
+ RChoice::Choice.setup_mongo_persist :optional, :name, :options
2
+ RChoice::Option.setup_mongo_persist :base_obj
3
+
4
+ class Choices
5
+ class << self
6
+ fattr(:list) { [] }
7
+ def setup_chooser!
8
+ Ability::CardChoice.chooser = lambda { |choice| self.list << choice }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,74 @@
1
+ require 'json'
2
+ module JsonPersist
3
+ def as_json(*args)
4
+ to_json_hash
5
+ end
6
+ def new_hash_json(attr,h,obj)
7
+ if obj.can_mongo_convert?
8
+ if obj.respond_to?(:select) && false
9
+ h.merge(attr => obj.to_mongo_hash)
10
+ elsif [Numeric,String].any? { |c| obj.kind_of?(c) }
11
+ h.merge(attr => obj)
12
+ else
13
+ h.merge(attr => obj.as_json)
14
+ end
15
+ else
16
+ h
17
+ end
18
+ rescue
19
+ return h
20
+ end
21
+ def to_json_hash
22
+ res = mongo_child_attributes.inject({}) do |h,attr|
23
+ obj = send(attr)
24
+ #raise "#{attr} is nil" unless obj
25
+ new_hash_json(attr,h,obj)
26
+ end.merge("_mongo_class" => self.class.to_s)
27
+ klass.mongo_reference_attributes.each do |attr|
28
+ val = send(attr)
29
+ res[attr] = val.to_mongo_ref_hash if val
30
+ end
31
+
32
+ if respond_to?(:addl_json_attributes) && true
33
+ puts "in addl_json_attributes part"
34
+ addl = [addl_json_attributes].flatten.select { |x| x }
35
+ addl.each do |attr|
36
+ puts "addl attr #{attr}"
37
+ res = new_hash_json(attr,res,send(attr))
38
+ end
39
+ end
40
+ res
41
+ end
42
+ def to_json(*args)
43
+ as_json(*args).to_json
44
+ end
45
+ end
46
+
47
+ module MongoHash
48
+ def as_json(*args)
49
+ res = {}
50
+ each do |k,v|
51
+ v = v.as_json(*args) if v.respond_to?(:as_json)
52
+ res[k.safe_to_mongo_hash.to_mongo_key] = v
53
+ end
54
+ res
55
+ end
56
+ end
57
+
58
+ class Array
59
+ def as_json(*args)
60
+ map do |obj|
61
+ obj.respond_to?(:as_json) ? obj.as_json(*args) : obj
62
+ end
63
+ end
64
+ end
65
+
66
+ class Class
67
+ def setup_mongo_persist(*attrs)
68
+ include MongoPersist
69
+ include JsonPersist
70
+ define_method(:mongo_attributes) do
71
+ attrs.flatten.map { |x| x.to_s }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,14 @@
1
+ class TurnManager
2
+ include FromHash
3
+ setup_mongo_persist :current_side_index
4
+ attr_accessor :game
5
+ fattr(:current_side_index) { 0 }
6
+ def current_side
7
+ game.sides[current_side_index]
8
+ end
9
+ def advance!
10
+ current_side.end_turn!
11
+ self.current_side_index = current_side_index + 1
12
+ self.current_side_index = 0 if current_side_index >= game.sides.size
13
+ end
14
+ end