Skip to content

Instantly share code, notes, and snippets.

@takoeight0821
Created December 3, 2023 14:23
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 takoeight0821/f332804e582078b5fc8af6b04eb13333 to your computer and use it in GitHub Desktop.
Save takoeight0821/f332804e582078b5fc8af6b04eb13333 to your computer and use it in GitHub Desktop.
RPG EDSL
// アイテムやスキルの効果を、プログラム上のデータとして表現し、それを解釈・実行することで各パラメータを算出する。
// EDSL (Embedded Domain Specific Language 組み込みDSL) として知られているテクニック。
namespace Edsl
{
// Stats represents parameters of the character.
// Stats can only be calculated by Effect.execute() method.
record Stats(string Name, int HP, int MP, int ATK, int DEF);
// Condition represents the condition of the game.
// Condition can be calculated by Effect.execute() method.
// Condition also can be constructed by whole game routine.
abstract record Condition
{
abstract public bool Satisfies(Condition given);
public record Grabbed(Stats Enemy) : Condition
{
public override bool Satisfies(Condition given)
{
return given switch
{
Grabbed grabbed => Enemy.Name == grabbed.Enemy.Name,
_ => false
};
}
}
}
// Effect represents the effect of items or skills.
abstract record Effect
{
// execute() method calculates the stats and condition.
abstract public (Stats, Condition) Execute(Stats stats, Condition given);
// Damage represents the damage to stats.
public record Damage(int Value) : Effect
{
public override (Stats, Condition) Execute(Stats stats, Condition given)
{
return (stats with { HP = stats.HP - (Value - stats.DEF) }, given);
}
}
public record Heal(int Value) : Effect
{
public override (Stats, Condition) Execute(Stats stats, Condition given)
{
return (stats with { HP = stats.HP + Value }, given);
}
}
public record Charge(int Value) : Effect
{
public override (Stats, Condition) Execute(Stats stats, Condition given)
{
return (stats with { MP = stats.MP + Value }, given);
}
}
public record Buff(int Value) : Effect
{
public override (Stats, Condition) Execute(Stats stats, Condition given)
{
return (stats with { ATK = stats.ATK + Value }, given);
}
}
public record Guard(int Value) : Effect
{
public override (Stats, Condition) Execute(Stats stats, Condition given)
{
return (stats with { DEF = stats.DEF + Value }, given);
}
}
public record When(Condition Want, Effect Effect) : Effect
{
public override (Stats, Condition) Execute(Stats stats, Condition given)
{
if (given.Satisfies(Want))
{
return Effect.Execute(stats, given);
}
else
{
return (stats, given);
}
}
}
public record Then(Effect First, Effect Second) : Effect
{
public override (Stats, Condition) Execute(Stats stats, Condition given)
{
var (stats1, given1) = First.Execute(stats, given);
return Second.Execute(stats1, given1);
}
}
}
}
namespace Game
{
class Program
{
static void Main(string[] args)
{
var player = new Edsl.Stats("Player", 100, 100, 20, 0);
Console.WriteLine($"Player: {player}");
var enemy = new Edsl.Stats("Enemy", 45, 0, 20, 0);
Console.WriteLine($"Enemy: {enemy}");
var buffItem = new Edsl.Effect.Buff(10);
var guardItem = new Edsl.Effect.Guard(10);
// attack 10 damage, then attack 20 damage if grabbed
var grabAttack = (Edsl.Stats player) => new Edsl.Effect.Then(
new Edsl.Effect.Damage(player.ATK),
new Edsl.Effect.When(
new Edsl.Condition.Grabbed(enemy),
new Edsl.Effect.Damage(player.ATK * 2)
)
);
// enemy is grabbed
var condition = new Edsl.Condition.Grabbed(enemy);
// player use buffItem
var (player1, condition1) = buffItem.Execute(player, condition);
Console.WriteLine($"Player use buffItem: {player1}");
// enemy use guardItem
var (enemy1, condition2) = guardItem.Execute(enemy, condition1);
Console.WriteLine($"Enemy use guardItem: {enemy1}");
// enemy attacked by player with grabAttack
// damage is (30 - 10) + (30 * 2 - 10) = 20 + 50 = 70
var (enemy2, _) = grabAttack(player1).Execute(enemy1, condition2);
Console.WriteLine($"Enemy attacked: {enemy2}");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment