AI & Difficulty System
Overview
NewBrain is the intelligence behind BlocKIT. It decides which shapes to give you based on how you're playing.
Goal: Keep game fun - not too easy, not too hard!
What is NewBrain?
Think of it as a game designer watching you play:
Player doing great → Make it harder
Player struggling → Make it easier
Player having fun → Keep it balanced
How It Works
Tracking Player Performance
NewBrain tracks:
├── Lines cleared per turn
├── Combo streaks
├── Board fill percentage
├── Shapes used efficiency
└── Recent game history
Difficulty Levels
| Level | Description | Shape Selection |
|---|---|---|
| Easy | Beginner friendly | Small shapes, many combos |
| Normal | Balanced challenge | Mixed sizes, some combos |
| Hard | Experienced player | Large shapes, fewer combos |
| Expert | Pro mode | Awkward shapes, rare combos |
Dynamic Adjustment
Game start → Normal difficulty
↓
Track 10-20 turns
↓
Analyze performance:
- Clearing 3+ lines often? → Increase difficulty
- Struggling with placement? → Decrease difficulty
- Steady progress? → Keep current level
↓
Adjust shape pool
↓
Continue monitoring...
NewBrain Inspector
┌─ NewBrain ──────────────────────┐
│ Current Difficulty: Normal │
│ │
│ Performance Window: 10 turns │
│ Adjustment Speed: 0.1 │
│ │
│ Easy Threshold: 0.3 │
│ Normal Threshold: 0.6 │
│ Hard Threshold: 0.8 │
│ │
│ Combo Weight: 1.5 │
│ Board Fill Weight: 1.0 │
└──────────────────────────────────┘
Performance Metrics
Success Score Calculation
float CalculateSuccessScore()
{
float score = 0f;
// Lines cleared (0.0 - 1.0)
score += (linesCleared / maxPossibleLines) * 0.4f;
// Combo bonus (0.0 - 1.0)
score += (comboLength / 10f) * 0.3f;
// Efficiency (0.0 - 1.0)
score += (shapesUsedWell / totalShapes) * 0.3f;
return Mathf.Clamp01(score);
}
Score Interpretation
| Score | Meaning | Action |
|---|---|---|
| 0.0 - 0.3 | Struggling | Decrease difficulty |
| 0.3 - 0.7 | Balanced | Maintain difficulty |
| 0.7 - 1.0 | Excelling | Increase difficulty |
Shape Selection Strategy
Easy Mode Pool
Preferred shapes:
- Single cells: ■
- Small lines: ■ ■, ■ ■ ■
- Small squares: ■ ■
■ ■
Avoided shapes:
- Large awkward pieces
- Complex L/T shapes
Normal Mode Pool
Balanced mix:
- 30% small (1-2 cells)
- 50% medium (3-5 cells)
- 20% large (6+ cells)
All shape types available
Hard Mode Pool
Preferred shapes:
- Large awkward pieces
- 5+ cell shapes
- Complex configurations
Fewer:
- Single cells
- Simple lines
Combo Opportunity Control
Brain controls how often combos are possible:
// Easy: 40% chance for combo shape
if (difficulty == Easy && Random.value < 0.4f)
return FindComboShape();
// Normal: 25% chance
if (difficulty == Normal && Random.value < 0.25f)
return FindComboShape();
// Hard: 10% chance
if (difficulty == Hard && Random.value < 0.1f)
return FindComboShape();
Board State Analysis
Brain considers current board:
float AnalyzeBoardDifficulty()
{
// How full is board? (0.0 - 1.0)
float fillPercentage = FilledCells / TotalCells;
// How fragmented? (0.0 - 1.0)
float fragmentation = CalculateFragmentation();
// How many valid placements? (0.0 - 1.0)
float validSpaces = CountValidSpaces() / TotalCells;
return (fillPercentage + fragmentation - validSpaces) / 3f;
}
Fragmentation Calculation
Low fragmentation (easy):
■ ■ ■ ■ □ □ □ □
□ □ □ □ □ □ □ □
→ Large contiguous empty space
High fragmentation (hard):
■ □ ■ □ ■ □ ■ □
□ ■ □ ■ □ ■ □ ■
→ Scattered small spaces
Adaptive Learning
Short-term Memory (Current Session)
// Tracks last 10 turns
Queue<TurnResult> recentTurns = new();
void RecordTurn(TurnResult result)
{
recentTurns.Enqueue(result);
if (recentTurns.Count > 10)
recentTurns.Dequeue();
AdjustDifficulty();
}
Long-term Memory (Across Sessions)
// Save player skill level
PlayerPrefs.SetFloat("SkillLevel", currentDifficulty);
// Load on start
void Start()
{
float savedSkill = PlayerPrefs.GetFloat("SkillLevel", 0.5f);
SetInitialDifficulty(savedSkill);
}
Customization Examples
More aggressive difficulty scaling
// In NewBrain.cs
private const float ADJUSTMENT_SPEED = 0.2f; // Was 0.1
void AdjustDifficulty()
{
float target = CalculateTargetDifficulty();
currentDifficulty = Mathf.Lerp(
currentDifficulty,
target,
ADJUSTMENT_SPEED // Faster changes
);
}
Easier for beginners
// In NewBrain Inspector:
Easy Threshold: 0.5 // Was 0.3 (easier to stay easy)
Combo Weight: 2.0 // Was 1.5 (more combo opportunities)
Disable dynamic difficulty
// Lock to one difficulty
public void DisableAdaptation()
{
adaptationEnabled = false;
currentDifficulty = 0.5f; // Lock to Normal
}
Custom difficulty curve
float CustomDifficultyCurve(int turnsPlayed)
{
// Easy for first 50 turns
if (turnsPlayed < 50)
return 0.3f;
// Gradually increase
float progress = (turnsPlayed - 50) / 200f;
return Mathf.Lerp(0.3f, 0.8f, progress);
}
Debug Visualization
void OnGUI()
{
if (showDebug)
{
GUILayout.Label($"Difficulty: {currentDifficulty:F2}");
GUILayout.Label($"Success Score: {GetSuccessScore():F2}");
GUILayout.Label($"Board Fill: {GetBoardFill():F2}");
GUILayout.Label($"Last Combo: {lastComboLength}");
}
}
Testing Different Difficulties
void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
SetDifficulty(0.2f); // Easy
if (Input.GetKeyDown(KeyCode.Alpha2))
SetDifficulty(0.5f); // Normal
if (Input.GetKeyDown(KeyCode.Alpha3))
SetDifficulty(0.8f); // Hard
}
Events for UI
public event Action<float> OnDifficultyChanged;
void AdjustDifficulty()
{
// ... adjustment logic
OnDifficultyChanged?.Invoke(currentDifficulty);
}
// In UI script:
void Start()
{
NewBrain.Instance.OnDifficultyChanged += UpdateDifficultyDisplay;
}
void UpdateDifficultyDisplay(float difficulty)
{
if (difficulty < 0.4f)
difficultyText.text = "Easy";
else if (difficulty < 0.7f)
difficultyText.text = "Normal";
else
difficultyText.text = "Hard";
}
Balance Guidelines
For Casual Games
- High combo opportunity (30-40%)
- Slower difficulty increase
- Longer easy period
- More forgiving
For Hardcore Games
- Low combo opportunity (10-15%)
- Faster difficulty increase
- Shorter easy period
- More punishing
For Tournament/Endless
Performance Monitoring
public struct PlayerStats
{
public int gamesPlayed;
public int totalLinesCleared;
public int longestCombo;
public int highScore;
public float averageGameLength;
}
public PlayerStats GetStats()
{
// Return aggregated stats for analytics
}
What's Next?
- ⚙️ Customization - tune all parameters
- 🎯 Game Logic - how shapes are picked
- 📊 Scoring - points and combos
Design Philosophy
Perfect difficulty = Player feels challenged but always "one move away" from success!