class Mana
{
⋮
public int Blue { get; set; }
public bool IsBlue { get { return Blue > 0; } }
//
}
public string ShortString
{
get
{
StringBuilder sb = new StringBuilder();
if (Colorless > 0) sb.Append(Colorless);
if (Red > 0) sb.Append( 'R' .Repeat(Red));
if (Green > 0) sb.Append( 'G' .Repeat(Green));
if (Blue > 0) sb.Append( 'U' .Repeat(Blue));
if (White > 0) sb.Append( 'W' .Repeat(White));
if (Black > 0) sb.Append( 'B' .Repeat(Black));
if (HasX) sb.Append( "X" );
return sb.ToString();
}
}
public void PayFor(Mana cost)
{
if (cost.Red > 0) Red -= cost.Red;
if (cost.Blue > 0) Blue -= cost.Blue;
if (cost.Green > 0) Green -= cost.Green;
if (cost.Black > 0) Black -= cost.Black;
if (cost.White > 0) White -= cost.White;
int remaining = cost.Colorless;
while (remaining > 0)
{
if (Red > 0) { --Red; --remaining; continue ; }
if (Blue > 0) { --Blue; --remaining; continue ; }
if (Black > 0) { --Black; --remaining; continue ; }
if (Green > 0) { --Green; --remaining; continue ; }
if (White > 0) { --White; --remaining; continue ; }
if (Colorless > 0) { --Colorless; --remaining; continue ; }
Debug.Fail( "Should not be here" );
}
}
public int ConvertedManaCost
{
get
{
return Red + Blue + Green + Black + White + Colorless;
}
}
public bool EnoughToPay(Mana cost)
{
if (Red < cost.Red || Green < cost.Green || White < cost.White ||
Blue < cost.Blue || Black < cost.Black)
return false ;
// can we pay the colourless price?
return ((Red - cost.Red) + (Green - cost.Green) + (White - cost.White) +
(Blue - cost.Blue) + (Black - cost.Black) + Colorless) >= cost.Colorless;
}
2RG
line and get an object like Mana
. And here again - and the new rules, not only the mechanics, but also the records. After all, how to write a double mana symbol? Most likely this way: {WB}{WB}{WB}
. Vooot, and for this you need a parser.X
icon. For example, pay XG
and get X
life. In order not to produce entities, I still think that the essence of Mana
should be one. The situation with X
can be razrulit single ( public bool HasX
), but you can summarize a little, so if suddenly there is a card with a value of XY
, we do not have to rewrite all the logic. In addition, there are situations when you can pay for a certain X
only with mana of a certain color. This also needs to be taken into account.Mana
object from a string.MagicTheGathering.Entities
for entities such as Mana
. This assembly can ILMerge
with other assemblies written in C # or F #.MagicTheGathering.Meta
for our meta-abstractions that will "collect" our entities.[ManaType( "Green" , "G" , "Forest" )]
class Mana:
public def constructor ():
pass
class ManaTypeAttribute(AbstractAstAttribute):
colorName as string
colorAbbreviation as string
landName as string
public def constructor (colorName as StringLiteralExpression,
colorAbbreviation as StringLiteralExpression, landName as StringLiteralExpression):
self .colorName = colorName.Value
self .colorAbbreviation = colorAbbreviation.Value
self .landName = landName.Value
public override def Apply(node as Node):
AddField(node)
private def AddField(node as Node):
c = node as ClassDefinition
f = [|
$(colorName.ToLower()) as Int32
|]
c.Members.Add(f)
ClassDefinition
colorName
into the real name of the field) and a quote (the brackets [|
and |]
turn our construct into an element of the propertyBoo | C # |
[ManaType( "Green" , "G" , "Forest" )] | [Serializable] |
AddField()
:private def AddField(node as Node):
c = node as ClassDefinition
r = ReferenceExpression(colorName)
f = [|
[Property($r)]
$(colorName.ToLower()) as Int32
|]
c.Members.Add(f)
IsGreen
return us true
if the map is green and false
if not. This property we will meet again, because It interacts specifically with hybrid maps. Here is my first attempt to implement it:private def AddIndicatorProperty(node as Node):
c = node as ClassDefinition
r = ReferenceExpression(colorName)
f = [|
$( "Is" + colorName) as bool:
get :
return ($r > 0);
|]
c.Members.Add(f)
[Serializable]
public class Mana
{
// Fields
protected int green;
// Properties
public int Green
{
get
{
return this .green;
}
set
{
this .green = value ;
}
}
public bool IsGreen
{
get
{
return ( this .Green > 0);
}
}
}
class ManaSumAttribute(AbstractAstAttribute):
static public LandTypes as List = []
⋮
public def constructor (colorName as StringLiteralExpression,
⋮
ManaSumAttribute.LandTypes.Add( self .colorName)
class ManaSumAttribute(AbstractAstAttribute):
⋮
public override def Apply(node as Node):
c = node as ClassDefinition
root = [| Colorless |] as Expression
for i in range(LandTypes.Count):
root = BinaryExpression(BinaryOperatorType.Addition,
root, ReferenceExpression(LandTypes[i] as string))
p = [|
public ConvertedManaCost:
get :
return $root
|]
c.Members.Add(p)
ConvertedManaCost
property. Here is what we get:public int ConvertedManaCost
{
get
{
return (( this .Colorless + this .Green) + this .Red);
}
}
class HybridManaAttribute(AbstractAstAttribute):
static public LandTypes as List = []
public override def Apply(node as Node):
mergedTypes as List = []
for i in range(LandTypes.Count):
for j in range(LandTypes.Count):
unless (mergedTypes.Contains(string.Concat(LandTypes[i], LandTypes[j])) or
mergedTypes.Contains(string.Concat(LandTypes[j], LandTypes[i])) or
i == j):
mergedTypes.Add(string.Concat(LandTypes[i], LandTypes[j]))
// each merged type becomes a field+property pair
c = node as ClassDefinition
for n in range(mergedTypes.Count):
name = mergedTypes[n] as string
r = ReferenceExpression(name)
f = [|
[Property($r)]
$(name.ToLower()) as int
|]
c.Members.Add(f)
IsGreen
in the case of hybrid mana. After all, we can no longer keep them in the attributes of uniform mana, because At that time, nothing was known about the hybrid mana. Let's move them to a separate attribute. So, we need to use both hybrid and single properties in order to understand the color of the map.class ManaIndicatorsAttribute(AbstractAstAttribute):
public override def Apply(node as Node):
c = node as ClassDefinition
for i in range(ManaSumAttribute.LandTypes.Count):
basic = ManaSumAttribute.LandTypes[i] as string
hybridLands as List = []
for j in range(HybridManaAttribute.HybridLandTypes.Count):
hybrid = HybridManaAttribute.HybridLandTypes[j] as string
if (hybrid.Contains(basic)):
hybridLands.Add(hybrid)
rbasic = ReferenceExpression(basic.ToLower())
b = Block();
b1 = [| return true if $rbasic > 0 |]
b.Statements.Add(b1)
for k in range(hybridLands.Count):
rhybrid = ReferenceExpression((hybridLands[k] as string).ToLower())
b2 = [| return true if $rhybrid > 0 |]
b.Statements.Add(b2)
r = [|
$( "Is" + basic):
get :
$b;
|]
c.Members.Add(r)
IsXxx
property, but it works, although at the Reflector level such a mess is obtained.ToString()
:class ManaStringAttribute(AbstractAstAttribute):
public override def Apply(node as Node):
b = Block()
b1 = [|
sb.Append(colorless) if colorless > 0
|]
b.Statements.Add(b1)
for i in range(ManaTypeAttribute.LandTypes.Count):
land = ReferenceExpression((ManaTypeAttribute.LandTypes[i] as string ).ToLower())
abbr = StringLiteralExpression(ManaTypeAttribute.LandAbbreviations[i] as string )
b2 = [|
sb.Append($abbr) if $land > 0;
|]
b.Statements.Add(b2)
for j in range(HybridManaAttribute.HybridLandTypes.Count):
land = ReferenceExpression((HybridManaAttribute.HybridLandTypes[j] as string ).ToLower())
abbr = StringLiteralExpression( "{" +
(HybridManaAttribute.HybridLandAbbreviations[j] as string ) + "}" )
b3 = [|
sb.Append($abbr) if $land > 0;
|]
b.Statements.Add(b3)
b3 = [|
sb.Append( "X" ) if hasX
|]
m = [|
public override def ToString():
sb = StringBuilder();
$b
return sb.ToString()
|]
c = node as ClassDefinition
c.Members.Add(m)
2GG{RW}
line. Let's divide the manaparser into 3 parts - the analysis of base mana, hybrid mana, and "everything else". So, basic mana is not difficult to disassemble:// basic land cases are in a separate block
basicLandCases = Block()
for i in range(ManaTypeAttribute.LandTypes.Count):
name = ManaTypeAttribute.LandTypes[i] as string
abbr = ManaTypeAttribute.LandAbbreviations[i] as string
rAbbr = CharLiteralExpression( char .ToUpper(abbr[0]))
rName = ReferenceExpression(name)
case = [|
if ( char .ToUpper(spec[i]) == $rAbbr):
m.$rName = m.$rName + 1
continue
|]
basicLandCases.Statements.Add(case);
RG
or GR
) does not affect the parser. However, the solution is not very complicated:// hybrid land cases are in a much smarter block
hybridLandCases = Block()
for i in range(HybridManaAttribute.HybridLandTypes.Count):
name = HybridManaAttribute.HybridLandTypes[i] as string
abbr = HybridManaAttribute.HybridLandAbbreviations[i] as string
// build an appreviation literal
abbr1 = StringLiteralExpression(abbr)
abbr2 = StringLiteralExpression(abbr[1].ToString() + abbr[0].ToString())
case = [|
if (s == $abbr1 or s == $abbr2):
m.$name = m.$name + 1
continue
|]
hybridLandCases.Statements.Add(case)
X
symbol:// the method itself
method = [|
public static def Parse(spec as string) as Mana:
sb = StringBuilder()
cb = StringBuilder() // composite builder
inHybrid = false // set when processing hybrid mana
m = Mana()
for i in range(spec.Length):
if (inHybrid):
cb.Append(spec[i])
continue
if ( char .IsDigit(spec[i])):
sb.Append(spec[i])
continue ;
if (spec[i] == '{' ):
inHybrid = true
continue
if (spec[i] == '}' ):
raise ArgumentException( "Closing } without opening" ) if not inHybrid
inHybrid = false
s = cb.ToString().ToUpper()
raise ArgumentException( "Only two-element hybrids supported" ) if s.Length != 2
$hybridLandCases
raise ArgumentException( "Hybrid mana " + s + " is not supported" )
$basicLandCases
if ( char .ToUpper(spec[i]) == 'X' ):
m.HasX = true
continue ;
|]
// add it
c = node as ClassDefinition
c.Members.Add(method)
[ManaType( "Green" , "G" , "Forest" )]
[ManaType( "Red" , "R" , "Mountain" )]
[ManaType( "Blue" , "U" , "Island" )]
[ManaType( "Black" , "B" , "Swamp" )]
[ManaType( "White" , "W" , "Plains" )]
[ManaSum]
[HybridMana]
[ManaIndicators]
[ManaString]
[ManaParser]
class Mana:
[Property(Colorless)]
colorless as int
[Property(HasX)]
hasX as bool
Source: https://habr.com/ru/post/72721/
All Articles