# Scoundrel Card Game - Development Log

## Overview
This document chronicles the complete development process of the Scoundrel card game, built as an HTML/CSS/JavaScript single-page application with sprite-based graphics.

---

## Phase 1: Initial Game Creation

### Step 1: PDF Rules Extraction
**Task**: Extract game rules from Scoundrel.pdf
**Challenge**: Cannot use `read_file` tool directly on PDF files
**Solution**: Used `run_in_terminal` with Python's `pdfplumber` library to extract text content
```bash
python3 -c "import pdfplumber; pdf = pdfplumber.open('Scoundrel.pdf'); print('\\n'.join([page.extract_text() for page in pdf.pages]))"
```

### Step 2: Core Game Implementation
**Created**: `scoundrel.html` (initial 961 lines)
**Key Features Implemented**:
- Card deck with 4 suits:
  - Monsters: Clubs (♣) and Spades (♠) - deal damage
  - Weapons: Diamonds (♦) - reduce damage
  - Potions: Hearts (♥) - restore health
- Room system: Deal 4 cards, choose 3, 1 carries over
- Combat mechanics: barehanded vs weapon-based
- Health tracking (20 HP max)
- Score calculation
- Game state management

### Step 3: Preview Setup
**Action**: Started local HTTP server and configured preview
```bash
python -m http.server 8000
```
Set up preview browser at `http://localhost:8000`

---

## Phase 2: Bug Fixes

### Bug #1: Room Transition Card Carry-Over
**Issue**: Remaining card was moved to bottom of dungeon instead of staying in room
**User Feedback**: "seems the remaining card stays visible in the room area, is not bringing to the next room"
**Root Cause**: `enterRoom()` was dealing 4 new cards and moving remaining card to dungeon
**Fix**: Modified logic to calculate `cardsNeeded = 4 - this.room.length` and only deal that many cards
```javascript
enterRoom() {
    const cardsNeeded = 4 - this.room.length;
    const cardsToDeal = Math.min(cardsNeeded, this.dungeon.length);
    for (let i = 0; i < cardsToDeal; i++) {
        this.room.push(this.dungeon.shift());
    }
}
```

---

## Phase 3: Graphics Integration

### Step 1: Sprite Assets Integration
**Request**: Use graphic sprites from `graphics/` folder for UI enhancement
**Assets Available**:
- `card_back.png` - Skull and pentagram design
- `monster_clubs.png` - Horned beast
- `monster_spades.png` - Shadow wraith
- `weapon_diamonds.png` - Enchanted sword
- `potion_hearts.png` - Heart-shaped potion bottle
- `ui_frame.png` - Gothic frame
- `health_bar.png` - Health bar graphic

### Step 2: Card Sprite Display Implementation
**Approach**: Used CSS pseudo-elements (`::before`) for sprite layering
**Key CSS**:
```css
.card::before {
    content: '';
    position: absolute;
    width: 85%;
    height: 75%;
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    border-radius: 6px;
    overflow: hidden;
}
```

**Monster Sprite Differentiation**:
- Clubs monsters: `monster_clubs.png`
- Spades monsters: `monster_spades.png` (requires `.spades` CSS class)

```javascript
let cardClass = `card ${card.type}`;
if (card.type === 'monster' && card.suit === '♠') {
    cardClass += ' spades';
}
```

### Step 3: UI Refinements
**Changes Made**:
1. Background: Changed to dark blue gradient, removed UI frame
2. Card back: Used native dimensions with `background-size: cover`
3. Deck count: Hidden from display
4. Card layout: Black background with centered sprites using `background-size: contain`
5. Card size: Standardized to 140x196px
6. Corner clipping: Added `border-radius` and `overflow: hidden` to prevent white corners on weapon/potion sprites

---

## Phase 4: UI Layout Improvements

### Slain Monsters Display
**Initial Issue**: Monsters displayed on top of weapon card
**Fix 1**: Moved from `top: -40px` to `bottom: -60px`
**Fix 2**: Increased to `bottom: -70px` to prevent overlap
**Final Position**: Below equipped weapon card with proper spacing

### Card Display Format
**User Request**: Show suit symbol first with space before value
**Implementation**: `${card.suit} ${card.display}` (e.g., "♣ K")

### Slain Monsters Ordering
**Issue**: Newest monsters appeared on top (reversed order)
**Fix**: Removed `.slice().reverse()` from loop to maintain chronological order
**Result**: Newest slain monster appears at bottom of list

### Slain Monsters Simplification
**Change**: Display only the last slain monster instead of all previous ones
**Implementation**: 
```javascript
const lastMonster = this.slainMonsters[this.slainMonsters.length - 1];
slainHtml += `<div class="slain-monster">${lastMonster.suit} ${lastMonster.display}</div>`;
```

---

## Phase 5: Grid Layout Adjustment

### Horizontal Alignment
**Request**: Align dungeon deck, room, and weapon slot horizontally in fullscreen
**Changes**:
- Increased side columns from `120px` to `160px`
- Added `align-items: start` to grid
```css
.game-board {
    grid-template-columns: 160px 1fr 160px;
    align-items: start;
}
```

---

## Phase 6: Scoring System Evolution

### Original Scoring (from PDF rules)
- Win: Score = remaining health (+ potion value if at max health)
- Lose: Score = negative sum of remaining monsters

### Scoring Update #1: Real-time Display
**Issue**: Score showed "-" during gameplay, only calculated at game end
**Fix**: Changed to show health value during play, calculated score at end

### Scoring Update #2: New Formula (Health - 182 + Killed)
**User Request**: "Score = Current Health - 91*2 (all monsters) + killed monsters"
**Implementation**:
```javascript
calculateScore() {
    const totalMonsterValue = 182; // 91 * 2
    let killedMonstersValue = /* sum of discard + slainMonsters */;
    return this.health - totalMonsterValue + killedMonstersValue;
}
```
**Starting Score**: 20 - 182 + 0 = -162

### Scoring Update #3: Simplified Formula
**User Request**: Simpler scoring - just health + killed monsters
**Final Implementation**:
```javascript
calculateScore() {
    return this.health + this.getTotalMonsterSlainValue();
}
```
**Starting Score**: 20 + 0 = 20

---

## Phase 7: Score Information Display

### Evolution of Score Messages

#### Version 1: Embedded in Action Messages
**Format**: "Took 5 damage! Health: 15. Score: 20 (±X)"
**Issue**: Too much information in single message

#### Version 2: Specific Format Request
**Format**: "Health: X; Total Monster Slain: Y (+Z)"
**Implementation**: Displayed in action messages

#### Version 3: Separate Score Info Box
**User Request**: Move score info to separate box below message
**Implementation**:
1. Created `.score-info-area` and `.score-info` CSS classes
2. Added `<div id="score-info">` below message area
3. Created `updateScoreInfo(lastMonsterValue)` function
4. Separated combat messages from score display

**Final Format**: 
```
Score: Health: X + Total Value of Monster Slain: Y (+Z)
```
Where:
- X = current health
- Y = total value of all killed monsters
- Z = value of last killed monster (shown after kills)

### Score Info Box Styling
```css
.score-info {
    background: rgba(78, 204, 163, 0.15);
    border: 1px solid #4ecca3;
    padding: 12px 25px;
    border-radius: 8px;
    color: #4ecca3;
}
```

---

## Phase 8: Rules Documentation Update

### Updated "How to Play" Section
**Changes to reflect new scoring**:
- Added **Scoring** rule: "Score = Current Health + Total Value of All Monsters Killed. Starting score is 20."
- Updated **Win** condition: "Survive through the entire dungeon with the highest score possible"
- Simplified **Lose** condition: "Health reaches 0"
- Removed old references to negative scores and remaining monster calculations

---

## Technical Architecture

### File Structure
```
cardgame/
├── scoundrel.html (main game file, ~1031 lines)
├── graphics/
│   ├── card_back.png
│   ├── monster_clubs.png
│   ├── monster_spades.png
│   ├── weapon_diamonds.png
│   ├── potion_hearts.png
│   ├── ui_frame.png
│   └── health_bar.png
├── Scoundrel.pdf (original rules)
└── DEVELOPMENT_LOG.md (this file)
```

### Core Technologies
- **HTML5**: Single-page structure
- **CSS3**: Grid layout, pseudo-elements, gradients, animations
- **JavaScript ES6**: Class-based game logic
- **Google Fonts**: Cinzel font for gothic theme

### Key Design Patterns

#### 1. Sprite Rendering with Pseudo-elements
```css
.card::before {
    content: '';
    background-image: url('graphics/...');
    background-size: contain;
}
```

#### 2. Game State Management
```javascript
class Game {
    constructor() {
        this.health = 20;
        this.dungeon = [];
        this.room = [];
        this.discard = [];
        this.equippedWeapon = null;
        this.slainMonsters = [];
    }
}
```

#### 3. Dynamic UI Updates
```javascript
updateUI() {
    // Update stats display
    // Render room cards
    // Render weapon slot
    // Update score info
}
```

---

## Key Lessons Learned

### 1. PDF File Handling
**Issue**: Cannot use `read_file` on binary PDF files
**Solution**: Use command-line tools with `pdfplumber` library

### 2. Room Transition Logic
**Pitfall**: Easy to break card carry-over by resetting room array
**Solution**: Calculate delta and only deal needed cards

### 3. Sprite Corner Clipping
**Issue**: White corners visible on non-square sprites
**Solution**: Apply `border-radius` and `overflow: hidden` to pseudo-element

### 4. CSS Specificity for Sprites
**Issue**: Spades monsters showing clubs sprite
**Solution**: Add conditional CSS class based on suit

### 5. Score Display Evolution
**Learning**: Iterative refinement based on user feedback
**Result**: Separated concerns (messages vs. score info) for better UX

### 6. Search/Replace Safety
**Pitfall**: Attempting to replace identical text causes errors
**Solution**: Always ensure `original_text` differs from `new_text`

---

## Game Mechanics Summary

### Card Types & Values
- **Clubs (♣)**: Monsters J=11, Q=12, K=13, A=1, 2-10 (13 cards, total: 91)
- **Spades (♠)**: Monsters J=11, Q=12, K=13, A=1, 2-10 (13 cards, total: 91)
- **Diamonds (♦)**: Weapons J=11, Q=12, K=13, A=1, 2-10 (13 cards)
- **Hearts (♥)**: Potions J=11, Q=12, K=13, A=1, 2-10 (13 cards)

### Combat Rules
1. **Barehanded**: Take full monster damage, monster goes to discard
2. **With Weapon**: Take (monster - weapon) damage, monster added to slain list
3. **Weapon Constraint**: Can only kill monsters ≤ last slain monster value

### Turn Structure
1. Enter room (deal 4 cards, or 3 if 1 carried over)
2. Choose 3 cards to interact with
3. Remaining card stays for next room
4. Option to avoid room once (but not twice in a row)

### Scoring Formula (Final)
```
Score = Current Health + Total Value of All Monsters Killed
Starting Score = 20
```

---

## Development Statistics
- **Total Lines**: ~1038 lines (HTML + CSS + JavaScript)
- **Total Iterations**: ~35+ refinements
- **Bug Fixes**: 3 major issues resolved
- **UI Improvements**: 15+ visual enhancements
- **Scoring Revisions**: 3 major formula changes
- **Testing Features**: Health reset functionality

---

## Phase 9: Testing Features

### Health Reset for Testing
**User Request**: Double-click on health value to reset to 20 for easier gameplay testing
**Implementation**:
1. Added `ondblclick="game.resetHealth()"` to health display element
2. Added pointer cursor and tooltip: "Double-click to reset health to 20"
3. Created `resetHealth()` function:
```javascript
resetHealth() {
    if (this.gameOver) return;
    this.health = this.maxHealth;
    this.updateUI();
    this.showMessage('Health reset to 20 (testing mode)', 'info');
}
```

**Benefits**:
- No need to restart entire game for testing
- Quick recovery from near-death situations
- Easier to test late-game scenarios

---

## Deck Composition Clarification

### 44-Card Deck Explained
**User Question**: "Why 44 cards instead of 52?"

**Answer**: The Scoundrel game uses a custom 44-card deck for game balance:

**Deck Breakdown**:
- **Cards 2-10 for all suits**: 9 × 4 = 36 cards
  - Clubs (♣): 2-10 (9 monster cards)
  - Spades (♠): 2-10 (9 monster cards)  
  - Diamonds (♦): 2-10 (9 weapon cards)
  - Hearts (♥): 2-10 (9 potion cards)

- **Face cards (J, Q, K, A) only for Clubs and Spades**: 4 × 2 = 8 cards
  - Clubs (♣): J=11, Q=12, K=13, A=14 (4 cards)
  - Spades (♠): J=11, Q=12, K=13, A=14 (4 cards)

**Total: 44 cards**

**Design Rationale**:
- Limits maximum weapon damage to 10
- Limits maximum healing to 10
- Prevents overpowered weapon/potion combinations
- Forces strategic resource management
- Maintains challenge throughout gameplay

**Code Implementation**:
```javascript
createDeck() {
    // Cards 2-10 for all suits
    for (let value = 2; value <= 10; value++) {
        for (let suit of ['♣', '♠', '♦', '♥']) {
            this.dungeon.push({ value, suit, type: suits[suit], display: value.toString() });
        }
    }

    // Face cards only for monsters (Clubs and Spades)
    const faceCards = [
        { value: 11, display: 'J' },
        { value: 12, display: 'Q' },
        { value: 13, display: 'K' },
        { value: 14, display: 'A' }
    ];

    for (let face of faceCards) {
        for (let suit of ['♣', '♠']) {
            this.dungeon.push({ value: face.value, suit, type: 'monster', display: face.display });
        }
    }
}
```

---

## Phase 10: PDF Documentation and Favicon Integration

### PDF Rules Link Addition
**Task**: Add link to Scoundrel.pdf in "How to Play" section
**Implementation**:
- Added styled link at bottom of rules section
- Opens in new window with `target="_blank"`
- Styled with teal color (#4ecca3) and dashed underline
- Document icon 📄 for visual clarity

### Favicon Integration
**Task**: Add favicon for browser tab display
**Files Added**:
- `graphics/favicon.ico` (0.6KB)
- `graphics/favicon.png` (735.8KB)
**Implementation**:
```html
<link rel="icon" type="image/x-icon" href="graphics/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="graphics/favicon.png">
```

### Author Credits
**Task**: Add developer attribution
**Implementation**:
- Added credits section at bottom of page
- Developers: Ken Xu, Kelvin Tan, Siddharth (teal color)
- "with the help of Qoder" (gold color with robot emoji 🤖)
- Styled with border-top separator

---

## Phase 11: Mobile Version Development

### Mobile Optimization Strategy
**Goal**: Create iPhone-friendly version without scrolling
**Approach**: Create separate `scoundrel_m.html` file

### Step 1: Mobile Layout Restructuring
**File Created**: `scoundrel_m.html` (copied from scoundrel.html)
**Key Changes**:
1. **Viewport**: Added `maximum-scale=1.0, user-scalable=no`
2. **Title**: Changed to "Scoundrel - Mobile"
3. **Layout**: Single column, removed 3-column grid
4. **Dungeon Deck**: Hidden with `display: none`
5. **Padding**: Reduced from 20px to 10px
6. **Overflow**: Added `overflow-x: hidden`

### Step 2: UI Element Resizing
**Card Sizing**:
- Initial: 70x98px (50% of desktop)
- Final: 100x140px (after user feedback)
- Layout: 2x2 CSS Grid instead of flex
- Container: 320px max-width, centered

**Other Elements**:
- Header: 2.5em → 1.5em
- Stats: Reduced padding and font sizes
- Buttons: 12px/30px → 8px/16px padding
- Messages: 1.1em → 0.8em
- Score info: 1em → 0.75em

### Step 3: Rules Modal Popup
**Problem**: Rules section takes too much space
**Solution**: Convert to popup modal
**Implementation**:
1. Hidden rules section by default
2. Added floating "?" button (bottom-right, gold, 50px circle)
3. Created modal with rules content
4. Close button: Initially at bottom, moved to top-right corner
5. Modal scrollable with `max-height: 80vh`

**JavaScript Functions**:
```javascript
function showRulesModal() {
    document.getElementById('rules-modal').classList.add('active');
}

function closeRulesModal() {
    document.getElementById('rules-modal').classList.remove('active');
}
```

### Step 4: Mobile Detection and Redirect
**Task**: Auto-redirect mobile users to mobile version
**File Modified**: `scoundrel.html`
**Implementation**:
```javascript
(function() {
    const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    const isMobileScreen = window.innerWidth <= 768;
    
    if ((isMobile || isMobileScreen) && !window.location.href.includes('scoundrel_m.html')) {
        window.location.href = 'scoundrel_m.html';
    }
})();
```

### Step 5: Weapon Slot Refinements
**Iterations**:
1. Initial: 140x196px (same as desktop)
2. Compact: 80x110px (matched small cards)
3. Final: 110x155px (matched larger 2x2 cards)

**Slain Monster Label Positioning**:
- Issue: Blocking "Equipped Weapon" title and "Avoid" button
- Initial: `bottom: -70px` (desktop position)
- Attempt 1: `bottom: -35px`
- Attempt 2: `top: -25px, right: 5px`
- Attempt 3: `top: -25px, right: -20px`
- Final: `top: -22px, right: -60px`
- Changed to horizontal layout with auto width

### Step 6: Card Layout Evolution
**Initial**: Flex layout with 70x98px cards
**Problem**: Cards too small on mobile
**Solution 1**: Increased to 100x140px
**Solution 2**: Changed to 2x2 CSS Grid
```css
.room-cards {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
    max-width: 320px;
    margin: 0 auto;
}
```

### Step 7: Rules Modal UX Enhancement
**User Feedback**: "put the close button on top right corner"
**Implementation**:
- Removed bottom "Close" button
- Added circular × button at top-right
- Style: 35x35px, red background, absolute positioning
- `position: absolute; top: 10px; right: 10px; z-index: 100`

---

## Phase 12: Deployment Configuration

### OSS Upload Infrastructure
**Files Created**:
1. `oss_config.json` - Deployment configuration
2. `upload_to_oss.py` - Python upload script
3. `UPLOAD_README.md` - Documentation
4. `.gitignore` - Credential protection

### OSS Configuration Evolution
**Initial Setup**:
- Region: oss-us-west-1
- Endpoint: https://oss-us-west-1.aliyuncs.com
- Upload path: `scoundrel-game/`

**Updated to Singapore**:
- Region: oss-ap-southeast-1
- Endpoint: https://oss-ap-southeast-1.aliyuncs.com
- Upload path: `scoundrel-game/`

**Final Configuration**:
- Upload path changed to: `` (empty string, root of bucket)
- Reason: OSS doesn't allow paths starting with `/`
- Error encountered: `InvalidObjectName` when uploadPath was `/`

### Files Deployed
**Total**: 12 files
1. `scoundrel.html` → `index.html`
2. `scoundrel_v0.html` → `v0.html`
3. `scoundrel_m.html` → `mobile.html`
4. `graphics/card_back.png`
5. `graphics/monster_clubs.png`
6. `graphics/monster_spades.png`
7. `graphics/weapon_diamonds.png`
8. `graphics/potion_hearts.png`
9. `graphics/favicon.ico`
10. `graphics/favicon.png`
11. `Scoundrel.pdf`
12. `DEVELOPMENT_LOG.md`

### Live URLs
- **Desktop**: https://oss-ap-southeast-1.aliyuncs.com/qoder-contest/index.html
- **Mobile**: https://oss-ap-southeast-1.aliyuncs.com/qoder-contest/mobile.html
- **Original (v0)**: https://oss-ap-southeast-1.aliyuncs.com/qoder-contest/v0.html

---

## Development Statistics
- **Total Lines**: ~1113 lines (mobile), ~1050 lines (desktop)
- **Total Phases**: 12 major development phases
- **Total Iterations**: 40+ refinements
- **Bug Fixes**: 4 major issues resolved
- **UI Improvements**: 20+ visual enhancements
- **Scoring Revisions**: 3 major formula changes
- **Files Created**: 15+ files (HTML, graphics, docs, scripts)
- **Deployment Iterations**: 10+ OSS uploads

---

## Key Learnings and Best Practices

### Mobile Development
1. **Separate file approach** works better than responsive CSS for dramatically different layouts
2. **Auto-detection redirect** provides seamless user experience
3. **Touch-friendly sizing**: Minimum 100px cards for mobile usability
4. **Grid layout** (2x2) better than flex for consistent card positioning
5. **Modal patterns** essential for space-constrained mobile UI

### CSS Positioning Challenges
1. **Absolute positioning** requires careful z-index management
2. **Negative positioning** (`right: -60px`) useful for labels outside containers
3. **Iterative refinement** often needed to avoid UI overlaps

### OSS/Cloud Deployment
1. **Empty string** (`""`) for root path, not `/` (causes InvalidObjectName error)
2. **Config-driven deployment** enables easy regional switches
3. **Content-Type headers** important for proper MIME types
4. **Credential security** via .gitignore essential

### Game Design Insights
1. **Simplified scoring** (health + monsters) more intuitive than complex formulas
2. **Real-time score display** keeps players engaged
3. **Visual feedback** (score changes, slain monsters) improves UX
4. **Testing features** (double-click health reset) speed development
5. **Win condition precision** important - final room must be fully cleared

---

## Phase 13: iOS Web App Enhancements

### Issue: White Page on iPhone
**Problem**: Mobile version (scoundrel_m.html) displayed completely white page on iPhone 17 Pro Max

**Root Causes Identified**:
1. Missing height declarations for html/body elements causing rendering issues
2. No fallback fonts if Google Fonts failed to load
3. iOS-specific rendering quirks with viewport and text sizing

**Solutions Implemented**:
```css
/* iOS White Page Fix */
html {
    height: 100%;
    -webkit-text-size-adjust: 100%;
}

body {
    font-family: 'Cinzel', serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    min-height: 100vh;
    height: 100%;
    -webkit-font-smoothing: antialiased;
}
```

```javascript
// JavaScript Error Handler for Debugging
window.onerror = function(msg, url, lineNo, columnNo, error) {
    console.error('Error: ' + msg + '\nURL: ' + url + '\nLine: ' + lineNo + '\nColumn: ' + columnNo + '\nError object: ' + JSON.stringify(error));
    return false;
};
```

**Files Modified**: scoundrel_m.html
**Deployment**: Updated files deployed to OSS

### Issue: White Status Bar/Notification Island
**Problem**: When opened as standalone web app on iPhone, the status bar area (notification island) showed white color instead of matching the app's dark background

**Root Cause**: `apple-mobile-web-app-status-bar-style` was set to `black-translucent`, which allowed background to show through but rendered white on certain iOS versions

**Solution**: Changed status bar style to solid `black`
```html
<meta name="apple-mobile-web-app-status-bar-style" content="black">
```

**Note**: Users must remove and re-add the web app to home screen for status bar changes to take effect

**Files Modified**: scoundrel_m.html
**Deployment**: Updated files deployed to OSS

---

## Phase 14: Win Condition Fix

### Issue: Premature Game Victory
**Problem**: In the final round of the game where Cards Remaining = 0, the game was ending prematurely with 1 card still remaining in the room. Players expected to clear all cards before victory.

**Root Cause**: Win condition logic was checking `this.room.length <= 1` instead of `=== 0`, allowing the game to end when one card remained in the room.

**Locations of Bug**:
1. `enterRoom()` function (line ~716 in scoundrel.html)
2. `selectCard()` function (line ~905 in scoundrel.html)
3. Same locations in scoundrel_m.html

**Solution Implemented**:
Changed win condition check in both functions:
```javascript
// BEFORE (incorrect):
if (this.dungeon.length === 0 && this.room.length <= 1) {
    this.winGame();
}

// AFTER (correct):
if (this.dungeon.length === 0 && this.room.length === 0) {
    this.winGame();
}
```

**Game Logic Impact**:
- Players must now clear ALL cards from the final room to achieve victory
- More challenging and satisfying win condition
- Consistent with game rules expectations

**Files Modified**: 
- scoundrel.html (2 locations)
- scoundrel_m.html (2 locations)

**Deployment**: All fixed files deployed to OSS (12 files uploaded successfully)

---

## Future Enhancement Ideas
(Not implemented, but potential improvements)
- Sound effects for card interactions
- Progressive Web App (PWA) for offline play
- Difficulty levels (different monster distributions)
- Persistent high scores (localStorage or backend)
- Leaderboard system
- Social sharing of scores
- Keyboard shortcuts for actions
- Undo last action feature
- Statistics tracking (monsters killed, damage taken, etc.)
- Daily challenges
- Alternative card themes/skins
- Multiplayer mode

---

**Development Period**: February 2026 (Single Extended Session)
**Final Status**: Fully functional responsive game with desktop and mobile versions, polished UI, sprite graphics, and cloud deployment
**Team**: Ken Xu, Kelvin Tan, Siddharth (with AI assistance from Qoder)
