manabrew_engine/agent/
creature_evaluator.rs1use crate::card::Card;
7
8pub fn evaluate_creature(card: &Card) -> i32 {
13 evaluate_creature_full(card, true, true)
14}
15
16pub fn evaluate_creature_full(card: &Card, consider_pt: bool, consider_cmc: bool) -> i32 {
20 let mut value: i32 = 80;
21
22 if !card.is_token {
24 value += 20;
25 }
26
27 let power = if card.has_keyword("Prevent all combat damage that would be dealt by CARDNAME.")
28 || card.has_keyword("Prevent all damage that would be dealt by CARDNAME.")
29 || card
30 .has_keyword("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")
31 || card.has_keyword("Prevent all damage that would be dealt to and dealt by CARDNAME.")
32 {
33 0
34 } else if card.toughness_assigns_damage() {
35 card.toughness()
36 } else {
37 card.power()
38 };
39 let toughness = card.toughness();
40
41 if consider_pt {
42 value += power * 15;
43 value += toughness * 10;
44
45 if card.has_keyword("Daybound") && card.is_double_faced() {
47 value += power * 10;
48 }
49 }
50
51 if consider_cmc {
52 value += card.mana_value() * 5;
53 }
54
55 if card.has_flying() {
57 value += power * 10;
58 }
59 if card.has_keyword("Horsemanship") {
60 value += power * 10;
61 }
62 if card.has_keyword("CARDNAME can't be blocked.") {
64 value += power * 10;
65 } else {
66 if card.has_keyword("Fear") {
67 value += power * 6;
68 }
69 if card.has_keyword("Intimidate") {
70 value += power * 6;
71 }
72 if card.has_keyword("Menace") {
73 value += power * 4;
74 }
75 if card.has_keyword("Skulk") {
76 value += power * 3;
77 }
78 }
79
80 if power > 0 {
82 if card.has_keyword("Double Strike") {
83 value += 10 + (power * 15);
84 } else if card.has_first_strike() {
85 value += 10 + (power * 5);
86 }
87 if card.has_deathtouch() {
88 value += 25;
89 }
90 if card.has_lifelink() {
91 value += power * 10;
92 }
93 if power > 1 && card.has_keyword("Trample") {
94 value += (power - 1) * 5;
95 }
96 if card.has_keyword("Vigilance") {
97 value += (power * 5) + (toughness * 5);
98 }
99 if card.has_keyword("Infect") {
100 value += power * 15;
101 } else if card.has_keyword("Wither") {
102 value += power * 10;
103 }
104 value += keyword_magnitude(card, "Toxic") * 5;
105 value += keyword_magnitude(card, "Afflict") * 5;
106 value += keyword_magnitude(card, "Rampage");
107 }
108
109 value += keyword_magnitude(card, "Annihilator") * 50;
110 value += keyword_magnitude(card, "Absorb") * 11;
111
112 if card.has_keyword("Outlast") {
114 value += 10;
115 }
116 value += keyword_magnitude(card, "Bushido") * 16;
117 value += keyword_count(card, "Flanking") * 15;
118 value += keyword_count(card, "Exalted") * 15;
119 value += keyword_count(card, "Melee") * 18;
120 value += keyword_count(card, "Prowess") * 5;
121
122 if card.has_keyword("Reach") && !card.has_flying() {
124 value += 5;
125 }
126
127 if card.has_indestructible() {
129 value += 70;
130 }
131 if card.has_keyword("Prevent all damage that would be dealt to CARDNAME.") {
134 value += 60;
135 } else if card.has_keyword("Prevent all combat damage that would be dealt to CARDNAME.") {
136 value += 50;
137 }
138 if card.has_hexproof() {
139 value += 35;
140 } else if card.has_keyword("Shroud") {
141 value += 30;
142 } else if card.has_keyword("Ward") {
143 value += 10;
144 }
145 if card.has_keyword("Protection") {
146 value += 20;
147 }
148
149 if card.has_keyword("Undying") || card.has_keyword("Persist") {
151 value += 30;
152 }
153
154 if card.has_defender() || card.has_keyword("CARDNAME can't attack.") {
156 value -= (power * 9) + 40;
157 }
158 if card.has_keyword("CARDNAME can't attack or block.") {
159 value = 50 + (card.mana_value() * 5); } else if card.has_keyword("CARDNAME can't block.") {
161 value -= 10;
162 }
163
164 if card.tapped && !card.can_untap() {
166 value = 50 + (card.mana_value() * 5); }
168
169 if !card.tapped {
170 value += 1; }
172
173 if card.activated_abilities.iter().any(|ab| ab.is_mana_ability) {
175 value += 10;
176 }
177
178 if card.has_keyword("Phasing") {
180 value -= 20.max(value / 2);
181 }
182
183 if card
185 .svars
186 .get("EndOfTurnLeavePlay")
187 .map(|v| v == "True")
188 .unwrap_or(false)
189 {
190 value -= 50;
191 }
192
193 value
194}
195
196pub fn is_useless_creature(card: &Card) -> bool {
200 if card.has_defender() {
201 return true;
202 }
203 if card.has_keyword("CARDNAME can't attack.") {
204 return true;
205 }
206 if card.has_keyword("CARDNAME can't attack or block.") {
207 return true;
208 }
209 if card.tapped && !card.can_untap() {
210 return true;
211 }
212 false
213}
214
215fn keyword_magnitude(card: &Card, kw: &str) -> i32 {
218 card.get_keyword_cost(kw)
219 .and_then(|s| s.parse::<i32>().ok())
220 .unwrap_or(0)
221}
222
223fn keyword_count(card: &Card, kw: &str) -> i32 {
226 card.keywords
227 .as_string_list()
228 .iter()
229 .filter(|k| k.starts_with(kw))
230 .count() as i32
231}