Skip to content

Instantly share code, notes, and snippets.

@amirrajan
Forked from eevee/encounters-meta.py
Last active March 30, 2022 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amirrajan/5934a3fa15d9d1e752f34d7e37a5d530 to your computer and use it in GitHub Desktop.
Save amirrajan/5934a3fa15d9d1e752f34d7e37a5d530 to your computer and use it in GitHub Desktop.
Enemy encounter definition with loot drop probabilities: Python vs JS vs Lua vs Ruby
# Python version
class SnarlingBeastEvent(EncounterEvent):
title = "a snarling beast"
text = "a snarling beast leaps out of the underbrush."
enemy = 'snarling_beast'
damage = 1
health = 5
attack_delay = 1
hit = 0.8
# making this a def that returns a literal is an odd choice;
# here's something more typed
loot = MultiLoot(
Loot(Fur, min=1, max=3, chance=1),
Loot(Meat, min=1, max=3, chance=1),
Loot(Teeth, min=1, max=3, chance=0.8),
)
-- Lua doesn't have built-in classes, just a toolbox of bits you can use to make them, so this will vary
local SnarlingBeastEvent = EncounterEvent:extend{
title = "a snarling beast",
text = "a snarling beast leaps out of the underbrush.",
enemy = 'snarling_beast',
damage = 1,
health = 5,
attack_delay = 1,
hit = 0.8,
loot = MultiLoot(
Loot{item=Fur, min=1, max=3, chance=1},
Loot{item=Meat, min=1, max=3, chance=1},
Loot{item=Teeth, min=1, max=3, chance=0.8}),
}
// ES6 classes don't support class attributes (STILL, ugh), but you can cheat a bit
function make_encounter(data) {
let encounter_type = Object.create(EncounterEvent.prototype);
for (let [k, v] of Object.entries(data)) {
encounter_type.prototype[k] = v;
}
return encounter_type;
}
local SnarlingBeastEvent = make_encounter({
title: "a snarling beast",
text: "a snarling beast leaps out of the underbrush.",
enemy: 'snarling_beast',
damage: 1,
health: 5,
attack_delay: 1,
hit: 0.8,
// ok js syntax kinda sucks here
loot: new MultiLoot(
new Loot(Fur, 1, 3, 1.0),
new Loot(Meat, 1, 3, 1.0),
new Loot(Teeth, 1, 3, 0.8),
),
});
# Ruby version (ideal/cleanest version)
class SnarlingBeastEvent < EncounterEvent
title "a snarling beast"
text "a snarling beast leaps out of the underbrush."
enemy :snarling_beast
damage 1
health 5
attack_delay 1
hit 0.8
loot Fur, min: 1, max: 3, chance: 1
loot Meat, min: 1, max: 3, chance: 1
loot Teeth, min: 1, max: 3, chance: 0.8
end
# Evolution of the ideal Ruby version
# Completely vanilla syntax
class SnarlingBeastEvent < EncounterEvent
def initialize
@title = "a snarling beast"
@text = "a snarling beast leaps out of the underbrush."
@enemy = :snarling_beast
@damage = 1
@health = 5
@attack_delay = 1
@hit = 0.8
# taking your lead on the typed definition
@loot = MultiLoot.new Loot.new(Fur, min: 1, max: 3, chance: 1),
Loot.new(Meat, min: 1, max: 3, chance: 1),
Loot.new(Teeth, min: 1, max: 3, chance: 1)
end
end
# The Python version is syntactically cleaner in every way.
# But the version above really is meh. I don't want to create 50 encounters like that...
# You can make the initalization a little cleaner by using a constructor
# with named parameters and pushing the MultiLoot, Loot repetion into
# the base class.
# Still no meta programming involved.
# Any named parameters not provided can be defaulted (or can raise an exception).
# This version is alright I guess.
class SnarlingBeastEvent < EncounterEvent
def initialize
super title: "a snarling beast",
text: "a snarling beast leaps out of the underbrush.",
enemy: :snarling_beast,
damage: 1,
health: 5,
attack_delay: 1,
hit: 0.8,
# PS: Notice that multi_loot (and by extension its default value) are array types.
# Ref types as default values in Python are a special type of wtf.
multi_loot: [
{ loot: Fur, min: 1, max: 3, chance: 1 },
{ loot: Meat, min: 1, max: 3, chance: 1 },
{ loot: Teeth, min: 1, max: 3, chance: 0.8 }
]
end
end
# I wanted to still make it cleaner. This is what I essentially have in ADR source
# (still using your typed loot construct).
# Both this version and the version above do not use the `=`
# assignment. Which gives me the ability to # expand past just the assignment of attributes.
# This is the first solution that uses meta programming.
class SnarlingBeastEvent < EncounterEvent
title "a snarling beast"
text "a snarling beast leaps out of the underbrush."
enemy :snarling_beast
damage 1
health 5
attack_delay 1
hit 0.8
loot Fur, min: 1, max: 3, chance: 1
loot Meat, min: 1, max: 3, chance: 1
loot Teeth, min: 1, max: 3, chance: 0.8
end
# Here is an example of how title is meta programmed.
# I can understand how this can be perceived as clever/"too extra",
# but this extra-ness cleans up all of the encounter events significantly... which I have a lot of.
class EncounterEvent
def self.title value
define_method :title do
instance_variable_set :@title, value
end
end
end
# I liked your JS/Lua version too (and really do enjoy working with it's prototypical nature...
# just don't do any important math in js).
# This is how I'd do something comprable in Ruby.
# It uses significantly less meta programming that the version above, but isn't as clean as the version above.
EncounterEvent.create :SnarlingBeastEvent,
title: "a snarling beast",
text: "a snarling beast leaps out of the underbrush.",
enemy: :snarling_beast,
damage: 1,
health: 5,
attack_delay: 1,
hit: 0.8,
multi_loot: [
{ loot: Fur, min: 1, max: 3, chance: 1 },
{ loot: Meat, min: 1, max: 3, chance: 1 },
{ loot: Teeth, min: 1, max: 3, chance: 0.8 }
]
# Supporting meta programming for the api above to work
class EncounterEvent
def self.create encounter_name, opts
Object.const_set encounter_name, Class.new(EncounterEvent) do
def initialize
super opts
end
end
end
end
# readability of Ruby
puts "All unique Pythagorean Triples between 1 and 100 sorted by area of the triangle."
side_lengths = (1..100).to_a
triples =
side_lengths.product(side_lengths)
.map do |width, height|
[width,
height,
Math.sqrt(width ** 2 + height ** 2)]
end.find_all do |_, _, hypotenuse|
hypotenuse.to_i == hypotenuse
end.uniq do |triangle|
triangle.sort
end.map do |width, height, hypotenuse|
{ width: width,
height: height,
hypotenuse: hypotenuse,
area: (width * height) / 2 }
end.sort_by do |triangle|
triangle[:area]
end
triples.each do |triple|
puts "* Triple with area: #{triple[:area]}"
puts "** a: #{triple[:width]}"
puts "** b: #{triple[:height]}"
puts "** c: #{triple[:hypotenuse]}"
end