Skip to content

Commit a76d798

Browse files
committed
Feature: Increased neural network complexity in Asteroids Brain.
1 parent 3ef6812 commit a76d798

File tree

5 files changed

+80
-20
lines changed

5 files changed

+80
-20
lines changed

G33kShell.Desktop/Console/Screensavers/AI/AiBrainBase.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,15 @@ public abstract class AiBrainBase
1818
{
1919
[JsonProperty] private NeuralNetwork m_qNet;
2020

21+
public int InputSize { get; private set; }
22+
public int[] HiddenLayers { get; private set; }
23+
public int OutputSize { get; private set; }
24+
2125
protected AiBrainBase(int inputSize, int[] hiddenLayers, int outputSize)
2226
{
27+
InputSize = inputSize;
28+
HiddenLayers = hiddenLayers;
29+
OutputSize = outputSize;
2330
m_qNet = new NeuralNetwork(inputSize, hiddenLayers, outputSize, learningRate: 0.05);
2431
}
2532

G33kShell.Desktop/Console/Screensavers/AI/AiGameCanvasBase.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,21 @@ protected void TrainAi(ScreenData screen, Action<byte[]> saveBrainBytes)
5656
return;
5757
}
5858

59+
System.Console.WriteLine("Starting training...");
60+
var brain = CreateGame().Brain;
61+
System.Console.WriteLine($"Brain layers: {brain.InputSize} : {brain.HiddenLayers.ToCsv(' ')} : {brain.OutputSize}");
62+
5963
m_stopTraining = false;
6064
m_trainingTask = Task.Run(() =>
6165
{
6266
while (!m_stopTraining)
6367
TrainAiImpl(saveBrainBytes);
68+
69+
System.Console.WriteLine("Training complete.");
70+
System.Console.WriteLine("Summary:");
71+
System.Console.WriteLine($" Brain layers: {brain.InputSize} : {brain.HiddenLayers.ToCsv(' ')} : {brain.OutputSize}");
72+
System.Console.WriteLine($" Generations: {m_generation}");
73+
System.Console.WriteLine($" Rating: {m_savedRating:F1}");
6474
});
6575
}
6676

@@ -89,7 +99,7 @@ private void TrainAiImpl(Action<byte[]> saveBrainBytes)
8999
// Report summary of results.
90100
m_generation++;
91101
var veryBest = eliteGames[0];
92-
var stats = $"Gen {m_generation} | Pop {m_currentPopSize} | GOAT {m_savedRating:F1} | Best {veryBest.Rating:F1}";
102+
var stats = $"Gen {m_generation}|Pop {m_currentPopSize}|Rating {veryBest.Rating:F1}|GOAT {m_savedRating:F1}";
93103
var extraStats = veryBest.ExtraGameStats().Select(o => $" {o.Name}: {o.Value}").ToArray().ToCsv().Trim();
94104
if (!string.IsNullOrEmpty(extraStats))
95105
stats += $" | {extraStats}";

G33kShell.Desktop/Console/Screensavers/Asteroids/Brain.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class Brain : AiBrainBase
1717
{
1818
public const int BrainInputCount = 10;
1919

20-
public Brain() : base(BrainInputCount, [16, 8], 4)
20+
public Brain() : base(BrainInputCount, [32, 16], 4)
2121
{
2222
}
2323

G33kShell.Desktop/Console/Screensavers/Asteroids/Game.cs

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,68 @@ public class Game : AiGameBase
2626
private int m_leftTurns;
2727
private int m_rightTurns;
2828
private GameState m_gameState;
29-
private int m_lives = 3;
29+
private int m_lives;
30+
private int m_thrustTicks;
3031

3132
/// <summary>
3233
/// Score used for public display.
3334
/// </summary>
3435
public int Score { get; private set; }
3536

36-
public override double Rating =>
37-
m_bulletsFired * HitAccuracy + // Reward hit accuracy.
38-
(Ship.DistanceTravelled / 100.0).Clamp(0.0, 1.0) * 5.0 + // Reward moving a bit.
39-
(1.0 - (double)Math.Abs(m_leftTurns - m_rightTurns) / (m_leftTurns + m_rightTurns + 1)) * 5.0 + // Reward not spinning.
40-
Score / 1000.0; // Reward score.
37+
public override double Rating
38+
{
39+
get
40+
{
41+
if (Score == 0)
42+
return -50.0; // Penalize pacifists.
43+
44+
if (m_gameTicks > 2000 && m_bulletsFired < 5)
45+
return -100.0; // Penalize idle campers.
46+
47+
var accuracyScore = HitAccuracy * 25.0; // Strongly reward aiming carefully.
48+
var perfectHitBonus = m_perfectHits * 1.0; // Bigger incentive for intentional targeting.
49+
var shotDiscipline = -(m_bulletsFired - m_perfectHits) * 0.02; // Stronger spam punishment.
50+
var rawScore = Score / 180.0; // Increase actual game score importance.
51+
52+
// Reward repositioning, but penalize constant thrust with no balance.
53+
var thrustRatio = m_thrustTicks / (double)m_gameTicks;
54+
var repositioningBonus = thrustRatio > 0.2 && thrustRatio < 0.8 ? 5.0 : -2.0;
55+
56+
// Strongly penalize sustained unidirectional spinning:
57+
var turnReward = TurnEquality > 0.5 ? TurnEquality * 5.0 : -5.0;
58+
59+
// Introduce a simple collision-avoidance reward:
60+
var survivalBonus = m_gameTicks / 4500.0; // Survive longer by avoiding collisions.
61+
62+
return rawScore + accuracyScore + perfectHitBonus +
63+
shotDiscipline + repositioningBonus +
64+
turnReward + survivalBonus;
65+
}
66+
}
67+
68+
public override IEnumerable<(string Name, string Value)> ExtraGameStats()
69+
{
70+
yield return ("Score", Score.ToString());
71+
yield return ("HitAccuracy", HitAccuracy.ToString("P1"));
72+
yield return ("GameTicks", m_gameTicks.ToString());
73+
yield return ("TurnEquality", TurnEquality.ToString("P1"));
74+
yield return ("BulletsFired", m_bulletsFired.ToString());
75+
yield return ("PerfectHits", m_perfectHits.ToString());
76+
yield return ("LeftTurns", m_leftTurns.ToString());
77+
yield return ("RightTurns", m_rightTurns.ToString());
78+
yield return ("ThrustTicks", m_thrustTicks.ToString());
79+
}
80+
81+
private double TurnEquality
82+
{
83+
get
84+
{
85+
var totalTurns = m_leftTurns + m_rightTurns;
86+
if (totalTurns == 0)
87+
return 0.0; // No turns - no bonus.
88+
return 1.0 - (double)Math.Abs(m_leftTurns - m_rightTurns) / totalTurns;
89+
}
90+
}
4191

4292
public override bool IsGameOver => m_lives == 0 || m_gameTicks > 200_000;
4393
public Ship Ship { get; private set; }
@@ -63,6 +113,8 @@ public override Game ResetGame()
63113
m_gameTicks = 0;
64114
m_leftTurns = 0;
65115
m_rightTurns = 0;
116+
m_thrustTicks = 0;
117+
m_lives = 3;
66118

67119
EnsureMinimumAsteroidCount();
68120

@@ -93,6 +145,9 @@ public override void Tick()
93145

94146
// Move ship.
95147
Ship.Move();
148+
if (Ship.IsThrusting)
149+
m_thrustTicks++;
150+
96151
if (Ship.Turning == Ship.Turn.Left)
97152
m_leftTurns++;
98153
else if (Ship.Turning == Ship.Turn.Right)
@@ -178,12 +233,4 @@ public override void Tick()
178233
Ship.Turning = moves.Turn;
179234
Ship.IsThrusting = moves.IsThrusting;
180235
}
181-
182-
public override IEnumerable<(string Name, string Value)> ExtraGameStats()
183-
{
184-
yield return ("Score", Score.ToString());
185-
yield return ("HitAccuracy", HitAccuracy.ToString("P1"));
186-
yield return ("GameTicks", m_gameTicks.ToString());
187-
yield return ("DistanceTravelled", Ship.DistanceTravelled.ToString("F1"));
188-
}
189236
}

G33kShell.Desktop/Console/Screensavers/Asteroids/Ship.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ public enum Turn { None, Left, Right }
3434
public Vector2 Position { get; private set; }
3535

3636
public Vector2 Velocity { get; private set; } = Vector2.Zero;
37-
38-
public double DistanceTravelled { get; private set; }
39-
4037
public bool IsThrusting { get; set; }
4138
public Turn Turning { get; set; }
4239
public bool IsShooting { get; set; }
@@ -69,7 +66,6 @@ public void Move()
6966

7067
// Position.
7168
Position += Velocity;
72-
DistanceTravelled += Velocity.Length();
7369

7470
// Wrap to the arena.
7571
var newX = (Position.X + m_arenaWidth) % m_arenaWidth;

0 commit comments

Comments
 (0)