LOGGING = false
def log_print *strings
return unless LOGGING
print *strings
end
def log *strings
return unless LOGGING
puts *strings
end
class Game
attr_accessor :player1, :player2, :turn_count
def initialize(player1, player2)
@turn_count = 0
@player1, @player2 = player1, player2
@player1.game = self
@player2.game = self
end
def turn
@turn_count += 1
log "Turn #{@turn_count}:::"
@player1.turn(@player2)
@player2.turn(@player1) unless over?
end
def over?
@player1.hp < 1 || @player2.hp < 1 || @player1.cards.size == 0 || @player2.cards.size == 0
end
def deck_out?
@player1.cards.size == 0
end
def to_s
"#{@player1}\nvs.\n#{@player2}"
end
end
class Player
NO_SHIELD = ->(c) { log_print("#{c.damage},"); c.damage}
attr_accessor :hp, :quanta, :quanta_per_turn, :creatures_in_play, :card_hash,
:cards, :hand, :name, :shield, :destroy_shield_on_turn, :game
def initialize(name, hp, card_hash)
@card_hash = card_hash
@name = name
@hp = hp
@shield = NO_SHIELD
@quanta = @quanta_per_turn = @shield_cost = 0
@creatures_in_play = []
@cards = []
@card_hash.each do |ca,n|
@shield_cost = ca.cost if ca.is_a?(Shield)
n.times{ @cards << ca.dup}
end
@cards = @cards.shuffle
@cards = @cards.shuffle if cards[0..6].all?{|c| !c.is_a?(Pillar)}
@cards.push(Dead.new)
@hand = []
7.times { draw }
end
def shield?
shield != NO_SHIELD
end
def destroy_shield!
self.shield = NO_SHIELD
end
def damage_per_turn
creatures_in_play.inject(0){|acc,c| acc + c.damage }
end
def turn_count
game.turn_count
end
def first?
game.player1 == self
end
def draw
hand << cards.shift
end
def turn(enemy)
destroy_shield! if destroy_shield_on_turn == turn_count
draw
log inspect
@hand.sort_by{|c| c.cast_order}.each{|c| c.try_cast(self, enemy) }
log_print 'damage: '
enemy.hp -= creatures_in_play.inject(0){|acc, c| acc + enemy.shield.call(c)}
log "\n"
creatures_in_play.each{|c| c.thaw }
self.quanta += quanta_per_turn
log inspect, "\n"
end
def quanta_for_creatures
shield? || (@shield_cost <= quanta_per_turn) ? quanta : quanta - @shield_cost + quanta_per_turn
end
def deck_size
cards.size - 1
end
def inspect
#"~#{name} hp: #{hp}" q: #{quanta} cs: #{deck_size} c_in: #{creatures_in_play.inspect}~"
"~#{name} hp: #{hp} qpt: #{quanta_per_turn} q: #{quanta} cs: #{deck_size}
dpt: #{damage_per_turn} in play: #{creatures_in_play.inspect}
in hand: #{hand.inspect}~"
end
def to_s
"#{name}(#{card_hash.inspect})"
end
end
class Card
def try_cast(me, enemy)
cast!(me, enemy) if cast?(me, enemy)
end
def cast!(me, enemy)
card_cast(me, enemy)
me.hand.slice!(me.hand.index(self))
end
def to_s
self.class.to_s
end
def dup
self
end
end
class Shield < Card
attr_reader :cost
end
class Dead < Card
def cast?(me, enemy); true ; end
def cast!(me, enemy); end;
def cast_order; 0; end
end
class Pillar < Card
def cast?(me, enemy); true ; end
def card_cast(me, enemy)
me.quanta_per_turn += 1
end
def cast_order; 0; end
end
class Creature < Card
attr_accessor :health, :frozen
def initialize(cost, damage, health)
@cost, @damage, @health = cost, damage, health
@frozen = 0
end
def cast?(me, enemy); me.quanta_for_creatures >= @cost; end
def card_cast(me, enemy)
me.quanta -= @cost
me.creatures_in_play << self
end
def cast_order; 100 - damage; end
def to_s
"Creature(#{damage}|#{health})#{frozen? ? "f#{}" : ''}"
end
def damage
@frozen > 0 ? 0 : @damage
end
def freeze(turns)
@frozen = turns
end
def thaw; @frozen -= 1 if @frozen > 0; end
def frozen?
@frozen > 0
end
def dup
self.class.new(@cost, @damage, @health)
end
end
class Bolt < Card
attr_accessor :cost, :damage
def initialize(cost, damage)
@cost, @damage = cost, damage
end
def cast?(me, enemy); me.quanta >= cost && enemy.creatures_in_play.detect{|cc| cc.health <= damage && !cc.frozen? }; end
def card_cast(me, enemy)
me.quanta -= cost
target = enemy.creatures_in_play.sort_by{|cc| (cc.health > damage || cc.frozen? ? 1000 : 0) - cc.damage }.first
log "Bolt!!!!! #{target}"
enemy.creatures_in_play.slice!(enemy.creatures_in_play.index(target))
end
def cast_order; 2; end
end
class Explosion < Card
attr_accessor :cost
def initialize(cost)
@cost = cost
end
def cast?(me, enemy)
me.quanta >= cost && enemy.shield != Player::NO_SHIELD &&
# if the enemy's shield only has 1 turn left on it, don't destroy it, let it run out
(
(enemy.destroy_shield_on_turn != (me.first? ? me.turn_count: me.turn_count+1)) ||
# unless you can kill in one turn
(me.damage_per_turn > enemy.hp)
)
end
def card_cast(me, enemy)
me.quanta -= cost
log "explosion!!!!!"
enemy.destroy_shield!
end
def cast_order; 1; end
def to_s; "Explosion(#{cost})"; end
end
class DimShield < Shield
def initialize(lasting = 3, cost = 6)
@cost = cost
@lasting = lasting
end
def cast?(me, enemy); !me.shield? && me.quanta >= @cost && me.hp < 75; end
def card_cast(me, enemy)
me.quanta -= @cost
log "dim shield!!!!!!!!!!!!! #{me.turn_count}"
me.destroy_shield_on_turn = me.turn_count+@lasting
me.shield = ->(c) { log_print '0,'; 0}
end
def cast_order; 1; end
def to_s
"DimShield(#{@lasting},#{@cost})"
end
end
class DuskMantel < Shield
def initialize(block = 0.5, cost = 6)
@block, @cost = block, cost
end
def cast?(me, enemy); !me.shield? && me.quanta >= @block; end
def card_cast(me, enemy)
me.quanta -= @block
log "dusk mantel(#{@block})!!!!!!!! ";
me.shield = ->(c) { d = rand >= @block ? 0 : c.damage; log_print d, ','; d}
end
def cast_order; 1; end
end
class PfrShield < Shield
def initialize(freeze = 0.3, reduce = 2, cost = 7)
@freeze, @reduce, @cost = freeze, reduce, cost
end
def cast?(me, enemy); !me.shield? && me.quanta >= @cost; end
def card_cast(me, enemy)
me.quanta -= @cost
log "Pfr(#{@freeze}, #{@reduce})!!!!!! ";
me.shield = ->(c){
d = c.damage - @reduce
c.freeze(4) if d > 0 && rand < @freeze
log_print "#{d > 0 ? d : 0}#{c.frozen > 0 ? "f#{c.frozen - 1}" : ''},"
d
}
end
def cast_order; 1; end
def to_s; "PfrShield(#{@freeze},#{@reduce},#{@cost})"; end
end
class IceShield < PfrShield
def initialize(freeze = 0.3, reduce = 1, cost = 6)
super
end
def to_s; "IceShield(#{@freeze},#{@reduce},#{@cost})"; end
end
test_decks = [
# ->() {Player.new('6dim-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 4, DimShield.new => 6, Bolt.new(2,5) => 4)},
# ->() {Player.new('6dim ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 8, DimShield.new => 6)},
# ->() {Player.new('6pfr-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 4, PfrShield.new => 6, Bolt.new(2,5) => 4)},
# ->() {Player.new('6pfr ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 8, PfrShield.new => 6)},
# ->() {Player.new('6ice-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 4, IceShield.new => 6, Bolt.new(2,5) => 4)},
# ->() {Player.new('6ice ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 8, IceShield.new => 6)},
# ->() {Player.new('5dim-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 5, DimShield.new => 5, Bolt.new(2,5) => 4)},
# ->() {Player.new('5dim ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 9, DimShield.new => 5)},
# ->() {Player.new('5pfr-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 5, PfrShield.new => 5, Bolt.new(2,5) => 4)},
# ->() {Player.new('5pfr ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 9, PfrShield.new => 5)},
# ->() {Player.new('5ice-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 5, IceShield.new => 5, Bolt.new(2,5) => 4)},
# ->() {Player.new('5ice ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 9, IceShield.new => 5)},
# ->() {Player.new('4dim-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 6, DimShield.new => 4, Bolt.new(2,5) => 4)},
# ->() {Player.new('4dim ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 10, DimShield.new => 4)},
# ->() {Player.new('4pfr-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 6, PfrShield.new => 4, Bolt.new(2,5) => 4)},
# ->() {Player.new('4pfr ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 10, PfrShield.new => 4)},
# ->() {Player.new('4ice-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 6, IceShield.new => 4, Bolt.new(2,5) => 4)},
# ->() {Player.new('4ice ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 10, IceShield.new => 4)},
# ->() {Player.new('3dim-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 7, DimShield.new => 3, Bolt.new(2,5) => 4)},
# ->() {Player.new('3dim ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 11, DimShield.new => 3)},
# ->() {Player.new('3pfr-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 7, PfrShield.new => 3, Bolt.new(2,5) => 4)},
# ->() {Player.new('3pfr ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 11, PfrShield.new => 3)},
# ->() {Player.new('3pfr-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 7, IceShield.new => 3, Bolt.new(2,5) => 4)},
# ->() {Player.new('3ice ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 11, IceShield.new => 3)},
# ->() {Player.new('2dim-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 8, DimShield.new => 2, Bolt.new(2,5) => 4)},
# ->() {Player.new('2dim ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 12, DimShield.new => 2)},
# ->() {Player.new('2pfr-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 8, PfrShield.new => 2, Bolt.new(2,5) => 4)},
# ->() {Player.new('2pfr ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 12, PfrShield.new => 2)},
# ->() {Player.new('2ice-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 8, IceShield.new => 2, Bolt.new(2,5) => 4)},
# ->() {Player.new('2ice ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 12, IceShield.new => 2)},
->() {Player.new('1dim-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 9, DimShield.new => 1, Bolt.new(2,5) => 4)},
->() {Player.new('1dim ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 13, DimShield.new => 1)},
->() {Player.new('1pfr-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 9, PfrShield.new => 1, Bolt.new(2,5) => 4)},
->() {Player.new('1pfr ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 13, PfrShield.new => 1)},
->() {Player.new('1ice-4bolt', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 9, IceShield.new => 1, Bolt.new(2,5) => 4)},
->() {Player.new('1ice ', 100.0, Pillar.new => 16, Creature.new(5,5,5) => 13, IceShield.new => 1)},
]
test_decks.each do |test_deck|
damage = 0
wins = 0
losses = 0
deck_outs = 0
reps = 10000
start = ->() {
Game.new(
#Player.new('rush', 10000, Pillar.new => 16, Creature.new(5,5,5) => 14),
Player.new('blast', 10000, Pillar.new => 15, Creature.new(5,5,5) => 13, Explosion.new(3) => 2),
test_deck.call
)
}
reps.times do
game = start.call
game.turn until game.over?
damage += 10000 - game.player1.hp
wins += game.player1.hp <= 9900 ? 1 : 0
losses += game.player1.hp > 9900 && game.player2.hp <= 0 ? 1 : 0
deck_outs += game.player1.hp > 9900 && game.player2.hp > 0 ? 1 : 0
end
overkill = damage*1.0/reps
win_p = wins*100.0/reps
loss_p = losses*100.0/reps
deck_out_p = deck_outs*100.0/reps
puts "#{start.call.inspect} (#{reps} games)",
"win%: #{win_p} loss%:#{loss_p} deckout%: #{deck_out_p} overkill: #{overkill}\n\n"
end