EnemyMemoryComponent - UQcsse3200/2026-studio-3 GitHub Wiki
EnemyMemoryComponent
1. Overview
EnemyMemoryComponent is the central storage component of the Enemy Memory System.
It stores the player's observed combat behaviour during one battle and produces immutable PlayerMemory snapshots for enemy AI.
The component is attached to the player entity, with one instance created for each combat encounter.
PlayerTrackerComponent
↓ writes
EnemyMemoryComponent
↓ creates
PlayerMemory
↓ read by
EnemyBehaviourComponent
↓ passed through
EnemyAIContext
↓ consumed by
EnemyAI
EnemyMemoryComponent does not observe the player directly. Player behaviour is observed and classified by PlayerTrackerComponent.
2. Component Location
Source file:
source/core/src/main/com/csse3200/game/components/enemy/EnemyMemoryComponent.java
Package:
package com.csse3200.game.components.enemy;
The source directory must match the package declaration so that Java tools and SonarCloud can resolve the class correctly.
3. ECS Ownership
EnemyMemoryComponent is attached to the player entity by PlayerFactory.
flowchart TB
Factory[PlayerFactory]
Player[Player Entity]
Memory[EnemyMemoryComponent]
Tracker[PlayerTrackerComponent]
Stats[CombatStatsComponent]
Energy[EnergyComponent]
Factory --> Player
Player --> Memory
Player --> Tracker
Player --> Stats
Player --> Energy
Both PlayerFactory.createPlayer overloads attach the component:
.addComponent(new EnemyMemoryComponent())
.addComponent(new PlayerTrackerComponent())
This ensures that normally created players and players created with restored health receive the same memory infrastructure.
4. Responsibilities
EnemyMemoryComponent is responsible for:
- Counting attack cards played during the battle
- Counting skill cards played during the battle
- Counting cards played during the current turn
- Saving the number of cards played during the previous turn
- Tracking whether the player gained positive block during the current turn
- Tracking consecutive completed turns without block
- Resetting temporary state after turn settlement
- Producing immutable
PlayerMemorysnapshots
It is not responsible for:
- Listening to battle events
- Resolving runtime card instances
- Loading card configurations
- Inspecting
EffectConfig - Understanding battle phases
- Selecting enemy actions
- Applying enemy intents
- Persisting memory between battles
flowchart LR
Input[Classified player behaviour]
Memory[EnemyMemoryComponent]
State[Mutable battle memory]
Snapshot[PlayerMemory]
Input --> Memory
Memory --> State
State --> Snapshot
5. UML Class Diagram
classDiagram
class Component {
+create()
+update()
+dispose()
}
class EnemyMemoryComponent {
-int attackCardsPlayed
-int skillCardsPlayed
-int cardsPlayedThisTurn
-int cardsPlayedLastTurn
-int consecutiveTurnsWithoutBlock
-boolean gainedBlockThisTurn
+recordCardPlayed(cardType, gainedBlock)
+settlePlayerTurn()
+snapshot()
}
class PlayerMemory {
+int attackCardsPlayed
+int skillCardsPlayed
+int consecutiveTurnsWithoutBlock
+int cardsPlayedLastTurn
+favoursAttack()
+isUndefended()
+empty()
}
class PlayerTrackerComponent {
+connect()
-onCardPlayed()
-onPhaseChanged()
}
class EnemyBehaviourComponent {
+setEnemyMemory()
+rollIntent()
}
Component <|-- EnemyMemoryComponent
PlayerTrackerComponent --> EnemyMemoryComponent : writes
EnemyMemoryComponent --> PlayerMemory : creates
EnemyBehaviourComponent --> EnemyMemoryComponent : reads
6. Internal State
The component stores six mutable fields:
private int attackCardsPlayed;
private int skillCardsPlayed;
private int cardsPlayedThisTurn;
private int cardsPlayedLastTurn;
private int consecutiveTurnsWithoutBlock;
private boolean gainedBlockThisTurn;
State Categories
flowchart TB
Memory[EnemyMemoryComponent]
subgraph BattleTotals
Attack[attackCardsPlayed]
Skill[skillCardsPlayed]
end
subgraph TurnState
Current[cardsPlayedThisTurn]
Block[gainedBlockThisTurn]
end
subgraph TurnHistory
Previous[cardsPlayedLastTurn]
Streak[consecutiveTurnsWithoutBlock]
end
Memory --> Attack
Memory --> Skill
Memory --> Current
Memory --> Block
Memory --> Previous
Memory --> Streak
Field Definitions
| Field | Meaning | Reset Behaviour |
|---|---|---|
attackCardsPlayed |
Attack cards played during the battle | Reset when the component is destroyed |
skillCardsPlayed |
Skill cards played during the battle | Reset when the component is destroyed |
cardsPlayedThisTurn |
All cards played during the active player turn | Reset after turn settlement |
cardsPlayedLastTurn |
Cards played during the previous completed turn | Replaced after each settlement |
consecutiveTurnsWithoutBlock |
Consecutive completed turns without positive block | Reset when block is gained |
gainedBlockThisTurn |
Whether positive block was observed this turn | Reset after turn settlement |
7. Public API
The component exposes three public operations.
classDiagram
class EnemyMemoryComponent {
+recordCardPlayed(CardType cardType, boolean gainedBlock) void
+settlePlayerTurn() void
+snapshot() PlayerMemory
}
7.1 recordCardPlayed
public void recordCardPlayed(
CardType cardType,
boolean gainedBlock)
Records one successfully played card.
The caller must already have determined:
- The card play succeeded
- The card type
- Whether the card provided positive block
The method does not inspect a CardConfig.
7.2 settlePlayerTurn
public void settlePlayerTurn()
Finalises the current player turn.
It:
- Copies the current card count into
cardsPlayedLastTurn - Updates the no-block streak
- Clears the current-turn card count
- Clears the current-turn block flag
7.3 snapshot
public PlayerMemory snapshot()
Creates and returns an immutable snapshot of the current stored information.
8. Card Recording Logic
When recordCardPlayed is called, every valid card increases:
cardsPlayedThisTurn
Attack cards additionally increase:
attackCardsPlayed
Skill cards additionally increase:
skillCardsPlayed
Other card types count towards the current turn but do not increase attack or skill totals.
flowchart TD
Start[Record successful card]
Validate{Card type is valid}
Count[Increment cards this turn]
Type{Card type}
Attack[Increment attack total]
Skill[Increment skill total]
Other[No category total]
Block{Positive block}
Mark[Mark block gained]
Finish[Recording complete]
Start --> Validate
Validate -->|No| Finish
Validate -->|Yes| Count
Count --> Type
Type -->|Attack| Attack
Type -->|Skill| Skill
Type -->|Other| Other
Attack --> Block
Skill --> Block
Other --> Block
Block -->|Yes| Mark
Block -->|No| Finish
Mark --> Finish
The implementation uses:
switch (cardType) {
case ATTACK -> attackCardsPlayed++;
case SKILL -> skillCardsPlayed++;
default -> {
// Other card types still count towards this turn.
}
}
Supported card categories include:
ATTACKSKILLPOWERSTATUSCURSE
9. Block Tracking
The method receives a boolean indicating whether the card provided positive block:
recordCardPlayed(cardType, gainedBlock)
If any card provides block during the current turn:
gainedBlockThisTurn = true;
Later cards that do not provide block must not reset the value.
flowchart LR
Start[Turn starts]
False[Block flag false]
BlockCard[Block card played]
True[Block flag true]
AttackCard[Non-block card played]
StillTrue[Block flag remains true]
Settle[Turn settlement]
Start --> False
False --> BlockCard
BlockCard --> True
True --> AttackCard
AttackCard --> StillTrue
StillTrue --> Settle
This means the component records whether block was gained at least once during the turn.
It does not record the amount of block.
10. Turn Settlement Logic
settlePlayerTurn() is called only when the real player turn ends.
flowchart TD
Start[Settle player turn]
Copy[Copy current count to last turn]
Block{Block gained this turn}
ResetStreak[Reset streak to zero]
IncreaseStreak[Increase streak by one]
ClearCount[Clear current turn count]
ClearFlag[Clear block flag]
Complete[Settlement complete]
Start --> Copy
Copy --> Block
Block -->|Yes| ResetStreak
Block -->|No| IncreaseStreak
ResetStreak --> ClearCount
IncreaseStreak --> ClearCount
ClearCount --> ClearFlag
ClearFlag --> Complete
Equivalent logic:
cardsPlayedLastTurn = cardsPlayedThisTurn;
if (gainedBlockThisTurn) {
consecutiveTurnsWithoutBlock = 0;
} else {
consecutiveTurnsWithoutBlock++;
}
cardsPlayedThisTurn = 0;
gainedBlockThisTurn = false;
11. Turn State Transition Diagram
flowchart LR
Empty[Turn begins]
Tracking[Cards being recorded]
Ready[Ready for settlement]
Settled[Previous turn updated]
Next[Next turn begins]
Empty --> Tracking
Tracking --> Tracking
Tracking --> Ready
Empty --> Ready
Ready --> Settled
Settled --> Next
Next --> Tracking
A player may end a turn without playing any cards.
In that case:
cardsPlayedLastTurn = 0
The no-block streak still increases if no positive block was observed.
12. Write Sequence UML
The tracker is the only normal writer to the component.
sequenceDiagram
participant BC as BattleController
participant PT as PlayerTracker
participant BD as BattleDeck
participant CS as CardService
participant EM as EnemyMemory
BC->>PT: Successful card event
PT->>BD: Find card instance
BD-->>PT: CardInstance
PT->>CS: Find card definition
CS-->>PT: CardConfig
PT->>PT: Classify type and block
PT->>EM: recordCardPlayed
EnemyMemoryComponent receives already-classified information.
It does not know how the card was resolved.
13. Turn Settlement Sequence UML
sequenceDiagram
participant BC as BattleController
participant PT as PlayerTracker
participant EM as EnemyMemory
BC->>PT: Phase changed
PT->>PT: Check real turn ending
alt Real player turn ended
PT->>EM: settlePlayerTurn
EM->>EM: Save previous turn count
EM->>EM: Update no-block streak
EM->>EM: Reset temporary state
else Temporary phase transition
PT->>PT: Ignore transition
end
The tracker ignores temporary card-resolution transitions:
PLAYER_TURN to CARD_RESOLVING
CARD_RESOLVING to PLAYER_TURN
The memory component does not contain this phase logic.
14. Read Sequence UML
Enemy AI receives memory through EnemyBehaviourComponent.
sequenceDiagram
participant BC as BattleController
participant EB as EnemyBehaviour
participant EM as EnemyMemory
participant PM as PlayerMemory
participant EC as EnemyAIContext
participant AI as EnemyAI
BC->>EB: rollIntent
EB->>EM: snapshot
EM-->>EB: PlayerMemory
EB->>EC: Create context with memory
EB->>AI: decide with context
AI->>EC: getPlayerMemory
AI-->>EB: EnemyIntent
A new snapshot is requested for every enemy decision.
This ensures the AI receives the latest memory.
15. Immutable Snapshot
The component creates a new PlayerMemory value:
return new PlayerMemory(
attackCardsPlayed,
skillCardsPlayed,
consecutiveTurnsWithoutBlock,
cardsPlayedLastTurn);
flowchart LR
Mutable[EnemyMemoryComponent]
SnapshotOne[PlayerMemory One]
Update[Memory update]
SnapshotTwo[PlayerMemory Two]
Mutable -->|snapshot| SnapshotOne
Update --> Mutable
Mutable -->|snapshot| SnapshotTwo
Previously created snapshots remain unchanged after later updates.
This protects enemy AI from mutable shared state.
16. Multiple Enemy Readers
One memory component is shared by every enemy in the current battle.
flowchart TB
Player[Player Entity]
Memory[EnemyMemoryComponent]
BehaviourOne[EnemyBehaviour One]
BehaviourTwo[EnemyBehaviour Two]
BehaviourThree[EnemyBehaviour Three]
AIone[EnemyAI One]
AItwo[EnemyAI Two]
AIthree[EnemyAI Three]
Player --> Memory
Memory --> BehaviourOne
Memory --> BehaviourTwo
Memory --> BehaviourThree
BehaviourOne --> AIone
BehaviourTwo --> AItwo
BehaviourThree --> AIthree
All enemies observe the same player history.
Each enemy still owns:
- Its own
EnemyBehaviourComponent - Its own
EnemyAI - Its own current intent
- Its own turn state
- Its own AI-specific counters
- Its own AI-specific cooldowns
17. Component Lifecycle
The memory exists for one combat encounter.
flowchart LR
Created[Component created]
Empty[Empty memory]
Tracking[Tracking actions]
Reading[AI reads snapshots]
Finished[Battle finished]
Disposed[Component disposed]
Created --> Empty
Empty --> Tracking
Tracking --> Reading
Reading --> Tracking
Tracking --> Finished
Reading --> Finished
Finished --> Disposed
The lifecycle is:
PlayerFactorycreates the player.EnemyMemoryComponentstarts with zero values.PlayerTrackerComponentrecords successful actions.- Enemy AI reads snapshots.
- The battle ends.
- The player entity and memory component are disposed.
- The next battle receives a new empty component.
Memory is not currently saved between battles.
18. Example
Assume the player performs the following actions.
First Turn
Attack
Attack
Skill with block
Before settlement:
attackCardsPlayed = 2
skillCardsPlayed = 1
cardsPlayedThisTurn = 3
cardsPlayedLastTurn = 0
consecutiveTurnsWithoutBlock = 0
gainedBlockThisTurn = true
After settlement:
attackCardsPlayed = 2
skillCardsPlayed = 1
cardsPlayedThisTurn = 0
cardsPlayedLastTurn = 3
consecutiveTurnsWithoutBlock = 0
gainedBlockThisTurn = false