Skip to content

Commit 34977ca

Browse files
committed
Test on a full demo game
1 parent 5082a4c commit 34977ca

File tree

6 files changed

+142
-43
lines changed

6 files changed

+142
-43
lines changed

SukaLambdaEngineTests/StartGameTest.cs

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,34 @@ public void StartGame()
1515
controller
1616
);
1717
Assert.IsNotNull( controller.vm!.map );
18+
Assert.IsFalse(controller.vm.gameStarted);
1819
Character lakhesh = controller.vm.characters.First().Value;
19-
Assert.AreEqual(lakhesh.statusCommitted.Mobility, 2);
20+
Assert.AreEqual(lakhesh.statusCommitted.Mobility, 5);
2021
Tuple<ushort, ushort>? position = controller.vm.map.CharacterPosition(lakhesh, out _);
2122
Assert.IsTrue(position != null && position.Item1 == 0 && position.Item2 == 0);
2223
controller.cmdRouter.ExecuteCommand(
2324
"TestAccount",
24-
"/mv ESW".TrimStart().TrimStart('/'),
25+
"/mv ESWSSSEEEEE".TrimStart().TrimStart('/'),
2526
controller
2627
);
2728
controller.vm.ExecuteRound();
28-
Assert.AreEqual(lakhesh.statusCommitted.Mobility, 2);
29+
Assert.IsTrue(controller.vm.gameStarted);
30+
31+
Assert.AreEqual(lakhesh.statusCommitted.Mobility, 5);
2932
Assert.AreEqual(lakhesh.statusTemporary.Mobility, 0);
3033
position = controller.vm.map.CharacterPosition(lakhesh, out _);
31-
Assert.IsTrue(position != null && position.Item1 == 1 && position.Item2 == 1);
34+
Assert.IsTrue(position != null && position.Item1 == 3 && position.Item2 == 4);
3235
List<string> logs = controller.logCollector.PopGameLog();
33-
Assert.IsTrue(logs.Contains("Lakhesh A0 -> B1"));
36+
Assert.IsTrue(logs.Contains("Lakhesh A0 -> D4"));
3437
string rendered = controller.vm.map.RenderAsText(Language.cn);
35-
Assert.AreEqual(rendered, "ABCDEFGHIJKL\r\n仓仓草草口口口口口口林森 0\r\n仓菈草草口口口口口口口口 1\r\n草草林森林口口森口森林森 2\r\n口林森林森口口林口林森林 3\r\n口口口口口口口森口森林森 4\r\n林森林森口森林森口水水水 5\r\n森林口口口林森林口水森林 6\r\n林森口森林口口森口口口口 7\r\n森林口口口口口口口水森林 8\r\n水水林水口水水水水水林森 9\r\n水水水水口林森水森林森林 10\r\n林森口口口森林水水水水水 11\r\n森林口林森林口口口林森林 12\r\n林森口口口口口口口森林森 13\r\n森林森林森林森口口口口口 14".Replace("\r", ""));
38+
Assert.AreEqual(rendered, "ABCDEFGHIJKL\r\n仓仓草草口口口口口口林森 0\r\n仓仓草草口口口口口口口口 1\r\n草草林森林口口森口森林森 2\r\n口林森林森口口林口林森林 3\r\n口口口菈口口口森口森林森 4\r\n林森林森口森林森口水水水 5\r\n森林口口口林森林口水森林 6\r\n林森口森林口口森口口口口 7\r\n森林口口口口口口口水森林 8\r\n水水林水口水水水水水林森 9\r\n水水水水口林森水森林森林 10\r\n林森口口口森林水水水水水 11\r\n森林口林森林口口口林森林 12\r\n林森口口口口口口口森林森 13\r\n森林森林森林森口口口口口 14".Replace("\r", ""));
3639
_ = """
3740
ABCDEFGHIJKL
3841
仓仓草草口口口口口口林森 0
39-
仓菈草草口口口口口口口口 1
42+
仓仓草草口口口口口口口口 1
4043
草草林森林口口森口森林森 2
4144
口林森林森口口林口林森林 3
42-
口口口口口口口森口森林森 4
45+
口口口菈口口口森口森林森 4
4346
林森林森口森林森口水水水 5
4447
森林口口口林森林口水森林 6
4548
林森口森林口口森口口口口 7
@@ -51,6 +54,41 @@ 森林口林森林口口口林森林 12
5154
林森口口口口口口口森林森 13
5255
森林森林森林森口口口口口 14
5356
""";
57+
controller.cmdRouter.ExecuteCommand("TestAccount", "/mv EEEEEE".TrimStart().TrimStart('/'), controller);
58+
controller.vm.ExecuteRound();
59+
controller.cmdRouter.ExecuteCommand("TestAccount", "/water".TrimStart().TrimStart('/'), controller);
60+
controller.vm.ExecuteRound();
61+
Island68.GetWater getwater = (Island68.GetWater)lakhesh.skills.Where(v => v.GetType() == typeof(Island68.GetWater)).FirstOrDefault()!;
62+
Assert.IsFalse(getwater.hasWater);
63+
rendered = controller.vm.map.RenderAsText(Language.cn);
64+
Assert.AreEqual(controller.vm.map.CharacterPosition(lakhesh, out _), new Tuple<ushort, ushort>(8, 4));
65+
66+
controller.cmdRouter.ExecuteCommand("TestAccount", "/mv ES".TrimStart().TrimStart('/'), controller);
67+
controller.vm.ExecuteRound();
68+
controller.cmdRouter.ExecuteCommand("TestAccount", "/water".TrimStart().TrimStart('/'), controller);
69+
controller.vm.ExecuteRound();
70+
Assert.AreEqual(controller.vm.map.CharacterPosition(lakhesh, out _), new Tuple<ushort, ushort>(9, 4));
71+
Assert.IsTrue(getwater.hasWater);
72+
Assert.AreEqual(lakhesh.statusCommitted.Mobility, 3);
73+
74+
controller.cmdRouter.ExecuteCommand("TestAccount", "/mv LUUUU".TrimStart().TrimStart('/'), controller);
75+
controller.vm.ExecuteRound();
76+
Assert.AreEqual(controller.vm.map.CharacterPosition(lakhesh, out _), new Tuple<ushort, ushort>(8, 4));
77+
controller.cmdRouter.ExecuteCommand("TestAccount", "/mv UUUU".TrimStart().TrimStart('/'), controller);
78+
controller.vm.ExecuteRound();
79+
Assert.AreEqual(controller.vm.map.CharacterPosition(lakhesh, out _), new Tuple<ushort, ushort>(8, 1));
80+
controller.cmdRouter.ExecuteCommand("TestAccount", "/mv LLLL".TrimStart().TrimStart('/'), controller);
81+
controller.vm.ExecuteRound();
82+
controller.cmdRouter.ExecuteCommand("TestAccount", "/mv LLLL".TrimStart().TrimStart('/'), controller);
83+
controller.vm.ExecuteRound();
84+
Assert.AreEqual(controller.vm.map.CharacterPosition(lakhesh, out _), new Tuple<ushort, ushort>(2, 1));
85+
controller.cmdRouter.ExecuteCommand("TestAccount", "/mv LLLL".TrimStart().TrimStart('/'), controller);
86+
controller.vm.ExecuteRound();
87+
Assert.AreEqual(controller.vm.map.CharacterPosition(lakhesh, out _), new Tuple<ushort, ushort>(0, 1));
88+
logs = controller.logCollector.PopGameLog();
89+
Assert.IsTrue(logs.Contains("Game ended in 9 rounds."));
90+
Assert.AreEqual(logs.Where(v => v == "水を得る!").Count(), 1);
91+
Assert.IsTrue(controller.vm.gameEnded);
5492
}
5593
}
5694
}

src/Character/PreDefined/Lakhesh.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public class Lakhesh : Character
55
public Lakhesh(string accountId) : base(accountId)
66
{
77
this.persistedStatus = new CharacterData { id = Guid.NewGuid(), accountId = accountId, characterName = nameof(Lakhesh), experience=0 };
8-
this.statusCommitted = new() { HitPoint = 10, Mobility = 2 };
8+
this.statusCommitted = new() { HitPoint = 10, Mobility = 5 };
99
this.skills.Add(new MoveSkill(this));
1010
}
1111

src/Map/Map.cs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,22 @@ public abstract class MapBlock : IRenderText
5353
public ushort y { get; init; }
5454
public SukaLambdaEngine? vm { get; set; } // You can do something when the vm is set
5555
public List<MetaEffect> metaEffects = new();
56-
public Dictionary<Altitude, ushort> mobilityCost = new()
56+
public static Dictionary<Altitude, ushort> defaultMobilityCost = new()
5757
{
5858
{Altitude.Underwater, 100},{Altitude.Surface, 1},{Altitude.Air, 0},{Altitude.Space, 0},
5959
};
60+
public Dictionary<Altitude, ushort> mobilityCost = defaultMobilityCost;
6061
public MapBlock(ushort x, ushort y, SukaLambdaEngine? vm)
6162
{
6263
this.x = x; this.y = y;
6364
this.vm = vm;
6465
}
6566

67+
public bool AllowEntrancy(Character character,
68+
Heading?[] movements, ushort movementIndexEnteringThisBlock) => true;
69+
public bool AllowDeparture(Character character,
70+
Heading?[] movements, ushort movementIndexEnteringThisBlock) => true;
71+
6672
/// <summary>
6773
///
6874
/// </summary>
@@ -325,17 +331,22 @@ public void CharacterMove(Character character, Heading[] headings, ushort[] dist
325331
using var tx = conn.Database.BeginTransaction();
326332
{
327333
ushort x, y;
328-
Tuple<ushort, ushort>? currentPosition = CharacterPosition(character, out CharacterInMapData? charData);
329-
if (currentPosition == null || charData == null) return;
330-
x = currentPosition.Item1; y = currentPosition.Item2;
334+
Tuple<ushort, ushort>? src = CharacterPosition(character, out CharacterInMapData? charData);
335+
if (src == null || charData == null) return;
336+
x = src.Item1; y = src.Item2;
331337
for (ushort i = 0; i < headings.Length; ++i )
332338
{
333339
bool outOfMap = false;
334340
Heading plannedHeading = headings[i];
335341
Heading? headingResult = CheckMovingOutOfMap(x, y, plannedHeading);
336342
if (headingResult != plannedHeading) outOfMap = true;
337343
// You can also change distances by yourself!
338-
if (blocks.TryGetValue(currentPosition, out MapBlock? block))
344+
345+
Tuple<ushort, ushort> currentPosition = new(x, y);
346+
dynamic? block = null;
347+
if (this.blocks.ContainsKey(currentPosition))
348+
block = blocks[currentPosition];
349+
if (block != null)
339350
{
340351
if (outOfMap)
341352
block.OnCharacterMovingOutOfMapFromThisBlock(character, headings, i);
@@ -345,17 +356,35 @@ public void CharacterMove(Character character, Heading[] headings, ushort[] dist
345356
if (character.removedFromMap || character.statusTemporary.Mobility <= 0) break;
346357
}
347358
if (headingResult == null) continue;
348-
Tuple<ushort, ushort> src = new(x, y);
349359
Tuple<ushort, ushort> movement = ComputeMovement(character, headingResult, distances[i]);
350-
character.statusTemporary.Mobility -= this.blocks.TryGetValue(src, out MapBlock? b) ? distances[i] * b.mobilityCost.GetValueOrDefault(character.altitude, (ushort)1) : distances[i];
351-
x += movement.Item1;
352-
y += movement.Item2;
360+
361+
int mobilityCost;
362+
if (block != null && block.mobilityCost.ContainsKey(character.altitude))
363+
mobilityCost = distances[i] * block.mobilityCost[character.altitude];
364+
else
365+
mobilityCost = distances[i] * MapBlock.defaultMobilityCost[character.altitude];
366+
character.statusTemporary.Mobility -= mobilityCost;
367+
368+
if (block == null || block.AllowDeparture(character, headings, i))
369+
{
370+
x += movement.Item1;
371+
y += movement.Item2;
372+
}
353373
Tuple<ushort, ushort> destination = new(x, y);
354374
if (vm == null) break;
355-
character.OnMoveInMap(vm, src, plannedHeading, destination, headingResult);
375+
character.OnMoveInMap(vm, currentPosition, plannedHeading, destination, headingResult);
356376
if (character.removedFromMap || character.statusTemporary.Mobility <= 0) break;
357-
if (blocks.TryGetValue(destination, out MapBlock? blockTo))
358-
blockTo.OnCharacterMovingIn(character, headings, i);
377+
if (blocks.ContainsKey(destination)) // blockTo
378+
{
379+
block = blocks[destination];
380+
if (block != null && !block.AllowEntrancy(character, headings, i))
381+
{ // cancel movement
382+
x -= movement.Item1;
383+
y -= movement.Item2;
384+
break;
385+
}
386+
block.OnCharacterMovingIn(character, headings, i);
387+
}
359388
if (character.removedFromMap || character.statusTemporary.Mobility <= 0) break;
360389
}
361390
charData.positionX = x; charData.positionY = y;

src/Map/Predefined/Island68.cs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,39 +33,48 @@ public static bool Start(string account, string commandBody, RootController cont
3333
public class GetWater : Skill
3434
{
3535
public bool hasWater = false;
36+
public bool executionSuccess = false;
3637
public GetWater(Character owner) : base(owner) { }
3738

3839
public override List<NumericEffect> Execute(SkillExecution skillExecution, SukaLambdaEngine vm, object[]? metaArgs = null)
3940
{
40-
if (hasWater == false)
41-
{
42-
hasWater = true;
43-
owner.statusCommitted.Mobility -= (long)Math.Abs(owner.statusCommitted.Mobility * 0.4);
44-
}
45-
return new List<NumericEffect>();
46-
}
47-
48-
[InGameCommand("water", "w|water", "Get water within distance 1; Mobility -40%")]
49-
public override bool PlanUseSkill(string commandBody, SukaLambdaEngine vm)
50-
{
51-
if (hasWater || vm.map?.GetType() != typeof(Island68)) return false;
41+
List<NumericEffect> result = new();
42+
if (vm.map == null || hasWater) return result;
5243
Tuple<ushort, ushort>? position = vm.map.CharacterPosition(owner, out _);
53-
if (position == null) return false;
44+
if (position == null) return result;
5445
MapBlock? b;
5546
foreach (Tuple<ushort, ushort> coordinate in vm.map.AllCoordinatesWithinManhattanDistance(position, 1))
56-
{
5747
if (vm.map.blocks.TryGetValue(coordinate, out b)
5848
&& b.GetType() == typeof(Water))
5949
{
60-
vm.PrepareSkill(new SkillExecution(owner, this, new Character[] { }, null));
61-
return true;
50+
hasWater = true;
51+
executionSuccess = true;
52+
owner.statusCommitted.Mobility -= (long)Math.Abs(owner.statusCommitted.Mobility * 0.4);
53+
break;
6254
}
63-
}
64-
return false;
55+
return result;
56+
}
57+
58+
[InGameCommand("water", ".*", "Get water within distance 1; Mobility -40%")]
59+
public override bool PlanUseSkill(string commandBody, SukaLambdaEngine vm)
60+
{
61+
if (hasWater || vm.map?.GetType() != typeof(Island68)) return false;
62+
Tuple<ushort, ushort>? position = vm.map.CharacterPosition(owner, out _);
63+
if (position == null) return false;
64+
vm.PrepareSkill(new SkillExecution(owner, this, new Character[] { }, null));
65+
return true;
6566
}
6667

6768
public override string WriteLogAtStart(SukaLambdaEngine vm) => "";
68-
public override string WriteLogAtEnd(SukaLambdaEngine vm) => "水を得る!";
69+
public override string WriteLogAtEnd(SukaLambdaEngine vm)
70+
{
71+
if (executionSuccess)
72+
{
73+
executionSuccess = false;
74+
return "水を得る!";
75+
}
76+
return "";
77+
}
6978
public override string WriteLogForEffect(NumericEffect effect, SukaLambdaEngine vm) => "";
7079
}
7180
public Island68(string databasePath, SukaLambdaEngine? vm = null) : base(databasePath, 1, 1, vm)
@@ -101,6 +110,7 @@ public Island68(string databasePath, SukaLambdaEngine? vm = null) : base(databas
101110
case '森': InsertMapBlock(new Forest((ushort)columnIndex, (ushort)rowIndex)); break;
102111
case '草': InsertMapBlock(new Lawn((ushort)columnIndex, (ushort)rowIndex)); break;
103112
case '水': InsertMapBlock(new Water((ushort)columnIndex, (ushort)rowIndex)); break;
113+
// TODO: 井
104114
case '口': default: break;
105115
}
106116
;
@@ -109,12 +119,14 @@ public Island68(string databasePath, SukaLambdaEngine? vm = null) : base(databas
109119

110120
public class Warehouse : MapBlock
111121
{
122+
public new Dictionary<Altitude, ushort> mobilityCost = new() { { Altitude.Surface, 0 } };
112123
public Warehouse(ushort x, ushort y, SukaLambdaEngine? vm = null) : base(x, y, vm) { }
113124
public new string RenderAsText(Language lang) => "仓";
114125
}
115126

116127
public class Forest : MapBlock
117128
{
129+
public new Dictionary<Altitude, ushort> mobilityCost = new() { {Altitude.Surface, 3} };
118130
public Forest(ushort x, ushort y, SukaLambdaEngine? vm = null) : base(x, y, vm) { }
119131
public new string RenderAsText(Language lang) => "森林"[(x+y+((y*3<vm?.map?.height) ? 1 : 0)) % 2].ToString();
120132
}
@@ -125,6 +137,8 @@ public Lawn(ushort x, ushort y, SukaLambdaEngine? vm = null) : base(x, y, vm) {
125137
}
126138
public class Water : MapBlock
127139
{
140+
public new bool AllowEntrancy(Character character,
141+
Heading?[] movements, ushort movementIndexEnteringThisBlock) => false;
128142
public Water(ushort x, ushort y, SukaLambdaEngine? vm = null) : base(x, y, vm) { }
129143
public new string RenderAsText(Language lang) => "水";
130144
}

0 commit comments

Comments
 (0)