ascension 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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